The unified validation engine for Sinter.
This module provides the single validation pipeline that handles all types
of validation in Sinter. It processes schemas created by Sinter.Schema.define/2
and runs a clean, predictable validation process.
Validation Pipeline
- Input Validation - Ensure input is valid format
- Required Field Check - Verify all required fields are present
- Field Validation - Validate each field against its type and constraints
- Strict Mode Check - Reject unknown fields if strict mode enabled
- Post Validation - Run custom cross-field validation if configured
Design Philosophy
The validator focuses purely on validation - it does NOT perform data transformation. Any data transformation should be explicit in your application code, keeping your validation logic pure and your transformations visible.
Usage
schema = Sinter.Schema.define([
{:name, :string, [required: true, min_length: 2]},
{:age, :integer, [optional: true, gt: 0]}
])
# Basic validation
{:ok, validated} = Sinter.Validator.validate(schema, %{name: "Alice", age: 30})
# With coercion
{:ok, validated} = Sinter.Validator.validate(schema, %{name: "Alice", age: "30"}, coerce: true)
# Batch validation
data_list = [%{name: "Alice", age: 30}, %{name: "Bob", age: 25}]
{:ok, validated_list} = Sinter.Validator.validate_many(schema, data_list)
Summary
Functions
Validates data against a Sinter schema.
Validates data against a schema, raising an exception on failure.
Validates multiple data items against the same schema efficiently.
Validates a stream of data maps against a schema with memory efficiency.
Types
Functions
@spec validate(Sinter.Schema.t(), map(), validation_opts()) :: validation_result()
Validates data against a Sinter schema.
This is the core validation function that all other validation in Sinter ultimately uses. It implements a clean, predictable pipeline.
Parameters
schema- A schema created bySinter.Schema.define/2data- The data to validate (must be a map)opts- Validation options
Options
:coerce- Enable type coercion (default: false):strict- Override schema's strict setting:path- Base path for error reporting (default: [])
Returns
{:ok, validated_data}- Validation succeeded{:error, errors}- List of validation errors
Examples
iex> schema = Sinter.Schema.define([
...> {:name, :string, [required: true]},
...> {:age, :integer, [optional: true, gt: 0]}
...> ])
iex> Sinter.Validator.validate(schema, %{name: "Alice", age: 30})
{:ok, %{name: "Alice", age: 30}}
iex> Sinter.Validator.validate(schema, %{age: 30})
{:error, [%Sinter.Error{path: [:name], code: :required, ...}]}
@spec validate!(Sinter.Schema.t(), map(), validation_opts()) :: map() | no_return()
Validates data against a schema, raising an exception on failure.
Examples
iex> validated = Sinter.Validator.validate!(schema, data)
%{name: "Alice", age: 30}
# Raises Sinter.ValidationError on failure
@spec validate_many(Sinter.Schema.t(), [map()], validation_opts()) :: {:ok, [map()]} | {:error, %{required(integer()) => [Sinter.Error.t()]}}
Validates multiple data items against the same schema efficiently.
Parameters
schema- Schema to validate againstdata_list- List of data maps to validateopts- Validation options
Returns
{:ok, validated_list}if all validations succeed{:error, errors_by_index}if any validation fails
Examples
iex> data_list = [
...> %{name: "Alice", age: 30},
...> %{name: "Bob", age: 25}
...> ]
iex> Sinter.Validator.validate_many(schema, data_list)
{:ok, [%{name: "Alice", age: 30}, %{name: "Bob", age: 25}]}
@spec validate_stream(Sinter.Schema.t(), Enumerable.t(), validation_opts()) :: Enumerable.t()
Validates a stream of data maps against a schema with memory efficiency.
This function is designed for processing large datasets without loading everything into memory at once. Perfect for DSPEx teleprompter optimization on large training sets.
Parameters
schema- A schema created bySinter.Schema.define/2data_stream- An Enumerable of data maps to validateopts- Validation options
Returns
- A stream of
{:ok, validated_data}or{:error, [Error.t()]}tuples
Examples
iex> schema = Sinter.Schema.define([{:id, :integer, [required: true]}])
iex> data_stream = Stream.map(1..1000, &%{"id" => &1})
iex> results = Sinter.Validator.validate_stream(schema, data_stream)
iex> Enum.take(results, 3)
[
{:ok, %{id: 1}},
{:ok, %{id: 2}},
{:ok, %{id: 3}}
]Memory Efficiency
This function processes items one at a time and does not accumulate results, making it suitable for very large datasets that would not fit in memory.