Exdantic.Wrapper (exdantic v0.0.2)

View Source

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

Summary

Functions

Creates a wrapper schema that can handle multiple input formats.

Creates multiple wrapper schemas for batch validation.

Creates a temporary wrapper schema for validating a single value.

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

Converts a wrapper schema back to its JSON Schema representation.

Unwraps a validated result, extracting just the field value.

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

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

Validates multiple values using their respective wrapper schemas.

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

Gets metadata about a wrapper schema.

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

Types

wrapper_options()

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

wrapper_schema()

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

Functions

create_flexible_wrapper(field_name, type_spec, opts \\ [])

@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(field_specs, global_opts \\ [])

@spec create_multiple_wrappers(
  [{atom(), Exdantic.TypeAdapter.type_spec(), wrapper_options()}],
  wrapper_options()
) :: %{required(atom()) => 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(field_name, type_spec, opts \\ [])

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(type_spec, base_opts \\ [])

@spec create_wrapper_factory(Exdantic.TypeAdapter.type_spec(), wrapper_options()) ::
  (atom() ->
     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(wrapper_schema, opts \\ [])

@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(validated_result, field_name)

@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(wrapper_schema, data, field_name)

@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(wrapper_schema, data, field_name)

@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(wrappers, data)

@spec validate_multiple(%{required(atom()) => wrapper_schema()}, %{
  required(atom()) => term()
}) ::
  {:ok, %{required(atom()) => term()}}
  | {:error, %{required(atom()) => [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(field_name, type_spec, input, opts \\ [])

@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(wrapper_schema)

@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?(arg1)

@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