# `Exdantic.Wrapper`
[🔗](https://github.com/nshkrdotcom/exdantic/blob/v0.1.0/lib/exdantic/wrapper.ex#L1)

Temporary validation schemas for type coercion patterns.

This module supports the DSPy pattern of creating temporary, single-field
validation schemas for complex type coercion, equivalent to Pydantic's:
`create_model("Wrapper", value=(target_type, ...))`

Wrapper schemas are useful when you need to:
- Validate a single value against a complex type specification
- Apply field-level constraints and coercion
- Extract and unwrap validated values
- Perform temporary schema-based validation without defining a full schema

# `wrapper_options`

```elixir
@type wrapper_options() :: [
  required: boolean(),
  coerce: boolean(),
  constraints: [term()],
  description: String.t(),
  example: term(),
  default: term()
]
```

# `wrapper_schema`

```elixir
@type wrapper_schema() :: Exdantic.Runtime.DynamicSchema.t()
```

# `create_flexible_wrapper`

```elixir
@spec create_flexible_wrapper(
  atom(),
  Exdantic.TypeAdapter.type_spec(),
  wrapper_options()
) ::
  wrapper_schema()
```

Creates a wrapper schema that can handle multiple input formats.

This wrapper can accept:
- The raw value directly
- A map with the field name as key
- A map with string keys

## Parameters
  * `field_name` - The field name for the wrapper
  * `type_spec` - The type specification
  * `opts` - Wrapper options

## Examples

    iex> wrapper = Exdantic.Wrapper.create_flexible_wrapper(:age, :integer, coerce: true)
    iex> Exdantic.Wrapper.validate_flexible(wrapper, 25, :age)        # Raw value
    {:ok, 25}
    iex> Exdantic.Wrapper.validate_flexible(wrapper, %{age: 25}, :age) # Map with atom key
    {:ok, 25}
    iex> Exdantic.Wrapper.validate_flexible(wrapper, %{"age" => 25}, :age) # Map with string key
    {:ok, 25}

# `create_multiple_wrappers`

```elixir
@spec create_multiple_wrappers(
  [{atom(), Exdantic.TypeAdapter.type_spec(), wrapper_options()}],
  wrapper_options()
) :: %{required(atom()) =&gt; wrapper_schema()}
```

Creates multiple wrapper schemas for batch validation.

## Parameters
  * `field_specs` - List of {field_name, type_spec, opts} tuples
  * `global_opts` - Options applied to all wrappers

## Returns
  * Map of field_name => wrapper_schema

## Examples

    iex> specs = [
    ...>   {:name, :string, [constraints: [min_length: 1]]},
    ...>   {:age, :integer, [constraints: [gt: 0]]},
    ...>   {:email, :string, [constraints: [format: ~r/@/]]}
    ...> ]
    iex> wrappers = Exdantic.Wrapper.create_multiple_wrappers(specs)
    %{name: %DynamicSchema{...}, age: %DynamicSchema{...}, email: %DynamicSchema{...}}

# `create_wrapper`

```elixir
@spec create_wrapper(atom(), Exdantic.TypeAdapter.type_spec(), wrapper_options()) ::
  wrapper_schema()
```

Creates a temporary wrapper schema for validating a single value.

## Parameters
  * `field_name` - The name for the wrapper field (atom)
  * `type_spec` - The type specification for the field
  * `opts` - Wrapper configuration options

## Options
  * `:required` - Whether the field is required (default: true)
  * `:coerce` - Enable type coercion (default: false)
  * `:constraints` - Additional field constraints (default: [])
  * `:description` - Field description for documentation
  * `:example` - Example value for the field
  * `:default` - Default value if field is missing

## Returns
  * Wrapper schema that can be used for validation

## Examples

    iex> wrapper = Exdantic.Wrapper.create_wrapper(:result, :integer, coerce: true, constraints: [gt: 0])
    %Exdantic.Runtime.DynamicSchema{...}

    iex> wrapper = Exdantic.Wrapper.create_wrapper(:email, :string,
    ...>   constraints: [format: ~r/@/], description: "Email address")
    %Exdantic.Runtime.DynamicSchema{...}

# `create_wrapper_factory`

```elixir
@spec create_wrapper_factory(Exdantic.TypeAdapter.type_spec(), wrapper_options()) ::
  (atom() -&gt;
     wrapper_schema())
```

Creates a reusable wrapper factory for a specific type and constraints.

## Parameters
  * `type_spec` - The type specification for the wrapper
  * `base_opts` - Base options applied to all wrappers created by this factory

## Returns
  * Function that creates wrappers with the specified type and base options

## Examples

    iex> email_wrapper_factory = Exdantic.Wrapper.create_wrapper_factory(
    ...>   :string,
    ...>   constraints: [format: ~r/@/],
    ...>   description: "Email address"
    ...> )
    iex> user_email_wrapper = email_wrapper_factory.(:user_email)
    iex> admin_email_wrapper = email_wrapper_factory.(:admin_email, required: false)

# `to_json_schema`

```elixir
@spec to_json_schema(
  wrapper_schema(),
  keyword()
) :: map()
```

Converts a wrapper schema back to its JSON Schema representation.

## Parameters
  * `wrapper_schema` - The wrapper schema to convert
  * `opts` - JSON Schema generation options

## Returns
  * JSON Schema map representation of the wrapper

## Examples

    iex> wrapper = Exdantic.Wrapper.create_wrapper(:count, :integer, constraints: [gt: 0])
    iex> Exdantic.Wrapper.to_json_schema(wrapper)
    %{
      "type" => "object",
      "properties" => %{
        "count" => %{"type" => "integer", "exclusiveMinimum" => 0}
      },
      "required" => ["count"]
    }

# `unwrap_result`

```elixir
@spec unwrap_result(map(), atom()) :: term()
```

Unwraps a validated result, extracting just the field value.

Utility function for extracting values from wrapper validation results.

## Parameters
  * `validated_result` - Result from wrapper validation (map)
  * `field_name` - The field name to extract

## Returns
  * The unwrapped field value

## Examples

    iex> validated = %{score: 85}
    iex> Exdantic.Wrapper.unwrap_result(validated, :score)
    85

# `validate_and_extract`

```elixir
@spec validate_and_extract(wrapper_schema(), term(), atom()) ::
  {:ok, term()} | {:error, [Exdantic.Error.t()]}
```

Validates data using a wrapper schema and extracts the field value.

## Parameters
  * `wrapper_schema` - The wrapper schema created by create_wrapper/3
  * `data` - The data to validate (can be the raw value or a map)
  * `field_name` - The field name to extract from the validated result

## Returns
  * `{:ok, extracted_value}` on successful validation and extraction
  * `{:error, errors}` on validation failure

## Examples

    iex> wrapper = Exdantic.Wrapper.create_wrapper(:count, :integer, coerce: true)
    iex> Exdantic.Wrapper.validate_and_extract(wrapper, %{count: "42"}, :count)
    {:ok, 42}

    iex> Exdantic.Wrapper.validate_and_extract(wrapper, "42", :count)  # Auto-wrap
    {:ok, 42}

    iex> Exdantic.Wrapper.validate_and_extract(wrapper, %{count: "abc"}, :count)
    {:error, [%Exdantic.Error{...}]}

# `validate_flexible`

```elixir
@spec validate_flexible(wrapper_schema(), term(), atom()) ::
  {:ok, term()} | {:error, [Exdantic.Error.t()]}
```

Validates data against a flexible wrapper that can handle multiple input formats.

## Parameters
  * `wrapper_schema` - The wrapper schema
  * `data` - The input data (raw value, or map with atom/string keys)
  * `field_name` - The field name to extract

## Returns
  * `{:ok, validated_value}` on success
  * `{:error, errors}` on failure

# `validate_multiple`

```elixir
@spec validate_multiple(%{required(atom()) =&gt; wrapper_schema()}, %{
  required(atom()) =&gt; term()
}) ::
  {:ok, %{required(atom()) =&gt; term()}}
  | {:error, %{required(atom()) =&gt; [Exdantic.Error.t()]}}
```

Validates multiple values using their respective wrapper schemas.

## Parameters
  * `wrappers` - Map of field_name => wrapper_schema
  * `data` - Map of field_name => value to validate

## Returns
  * `{:ok, validated_values}` if all validations succeed
  * `{:error, errors_by_field}` if any validation fails

## Examples

    iex> wrappers = %{
    ...>   name: Exdantic.Wrapper.create_wrapper(:name, :string),
    ...>   age: Exdantic.Wrapper.create_wrapper(:age, :integer)
    ...> }
    iex> data = %{name: "John", age: 30}
    iex> Exdantic.Wrapper.validate_multiple(wrappers, data)
    {:ok, %{name: "John", age: 30}}

# `wrap_and_validate`

```elixir
@spec wrap_and_validate(
  atom(),
  Exdantic.TypeAdapter.type_spec(),
  term(),
  wrapper_options()
) ::
  {:ok, term()} | {:error, [Exdantic.Error.t()]}
```

Validates data using a wrapper schema and extracts the value in one step.

This is a convenience function that combines create_wrapper/3 and validate_and_extract/3.

## Parameters
  * `field_name` - The name for the wrapper field
  * `type_spec` - The type specification for the field
  * `input` - The data to validate
  * `opts` - Wrapper configuration options (same as create_wrapper/3)

## Returns
  * `{:ok, validated_value}` on successful validation
  * `{:error, errors}` on validation failure

## Examples

    iex> Exdantic.Wrapper.wrap_and_validate(:score, :integer, "85", coerce: true, constraints: [gteq: 0, lteq: 100])
    {:ok, 85}

    iex> Exdantic.Wrapper.wrap_and_validate(:email, :string, "invalid", constraints: [format: ~r/@/])
    {:error, [%Exdantic.Error{...}]}

    iex> Exdantic.Wrapper.wrap_and_validate(:items, {:array, :string}, ["a", "b", "c"])
    {:ok, ["a", "b", "c"]}

# `wrapper_info`

```elixir
@spec wrapper_info(wrapper_schema()) :: map()
```

Gets metadata about a wrapper schema.

## Parameters
  * `wrapper_schema` - The wrapper schema to inspect

## Returns
  * Map with wrapper metadata

## Examples

    iex> wrapper = Exdantic.Wrapper.create_wrapper(:email, :string, description: "User email")
    iex> Exdantic.Wrapper.wrapper_info(wrapper)
    %{
      is_wrapper: true,
      field_name: :email,
      field_count: 1,
      wrapper_type: :single_field,
      created_at: ~U[...]
    }

# `wrapper_schema?`

```elixir
@spec wrapper_schema?(term()) :: boolean()
```

Checks if a schema is a wrapper schema created by this module.

## Parameters
  * `schema` - The schema to check

## Returns
  * `true` if it's a wrapper schema, `false` otherwise

## Examples

    iex> wrapper = Exdantic.Wrapper.create_wrapper(:test, :string)
    iex> Exdantic.Wrapper.wrapper_schema?(wrapper)
    true

    iex> regular_schema = Exdantic.Runtime.create_schema([{:name, :string}])
    iex> Exdantic.Wrapper.wrapper_schema?(regular_schema)
    false

---

*Consult [api-reference.md](api-reference.md) for complete listing*
