# `RintoError.Changeset`
[🔗](https://github.com/RintoLab/rinto_error/blob/v0.2.0/lib/rinto_error/changeset.ex#L2)

Ecto field-error helpers, available when Ecto is present at library compilation.
See the [Ecto guide](guides/03_ecto_en.md).

The library supplies a standard `RintoError.Source` implementation for
`Ecto.Changeset`. Register it with `entry_for` like any other source type.
Construction retains the changeset in raw; payload conversion expands its
field errors with `to_errors/1`.

To provide your own implementation, set compile-time application configuration
`config :rinto_error, exclude_default_sources: [Ecto.Changeset]` and recompile
the dependency. These helpers remain available; see `RintoError.Source`.

`add_error/4` and `to_errors/1` also work without an Error module or registration.
Field classifications are independent of catalog kinds and output aliases.

# `add_error`

```elixir
@spec add_error(Ecto.Changeset.t(), atom(), atom(), keyword()) :: Ecto.Changeset.t()
```

Adds one field error to a changeset.

The classification must be an atom other than `nil`, `true`, or `false`;
invalid values raise `ArgumentError`.

`message` is taken from opts, or `Atom.to_string(error)` when absent.
The explicit classification overrides `:error` in opts. The remaining
opts are passed to Ecto unchanged.

# `to_errors`

```elixir
@spec to_errors(term()) :: [%{kind: atom(), data: map(), errors: []}]
```

Returns field payloads with kind, data, and empty errors; non-changesets return `[]`.

Nested associations and embeds are flattened into paths containing string field
names and zero-based list indices. Field messages remain in the original changeset.
No business Error structs are constructed or catalog mappings applied.

Type metadata uses `%{t: type}` for scalars, `%{t: :array | :map, of: type}`
recursively for collections, and `%{t: :enum, values: [string]}` for enums in
declaration order. Custom/parameterized types use their underlying Ecto type;
semantic names such as decimal, uuid, and date remain distinct. Length validation
keeps its counting type without inferring schema details. Cyclic types raise
`ArgumentError`; absent type metadata stays absent.

Inclusion/subset/exclusion candidates and number bounds use these conversions:

* Range: `%{t: :range, first: integer, last: integer, step: integer}`, not enumerated.
* Date.Range: the same shape with `t: :date_range` and ISO 8601 date bounds.
* MapSet: sorted lists; lists and ordinary maps: recursively converted values.
* Duration: ISO 8601 strings; Decimal: decimal strings.

Other values are left to the encoder. Other metadata and application Error data
are unchanged; original metadata remains in the source changeset.

---

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