# `GuardedStruct.Info`
[🔗](https://github.com/mishka-group/guarded_struct/blob/v0.1.4/lib/guarded_struct/info.ex#L1)

Runtime introspection of guardedstruct DSL state.

Built on `Spark.InfoGenerator`, which auto-generates accessors for every
section option in the DSL (e.g. `guardedstruct_enforce!/1`,
`guardedstruct_json!/1`). On top of those, this module exposes ergonomic
helpers so callers don't have to walk `__fields__/0` maps themselves.

## Helper categories

* **Field-level lookups** — `field_kind/2`, `field_default/2`,
  `field_derives/2`, `field_validator/2`, `field_auto/2`, `enforce?/2`,
  `virtual?/2`, `dynamic?/2`
* **Collections by kind** — `sub_fields/1`, `virtual_fields/1`,
  `dynamic_fields/1`, `conditional_fields/1`, `conditional_keys/1`,
  `pattern_keyed?/1`
* **Section-option shorthands** — `enforce?/1`, `opaque?/1`,
  `authorized_fields?/1`, `json?/1`, `error?/1`
* **Navigation** — `sub_module/2`, `conditional_children/2`

## Example

    defmodule MyApp.User do
      use GuardedStruct
      guardedstruct enforce: true do
        field :name, String.t()
        virtual_field :password_confirm, String.t()
        sub_field :address, struct() do
          field :city, String.t()
        end
      end
    end

    GuardedStruct.Info.fields(MyApp.User)            #=> [:name, :password_confirm, :address]
    GuardedStruct.Info.virtual_fields(MyApp.User)    #=> [:password_confirm]
    GuardedStruct.Info.sub_fields(MyApp.User)        #=> [:address]
    GuardedStruct.Info.field_kind(MyApp.User, :name) #=> :field
    GuardedStruct.Info.enforce?(MyApp.User, :name)   #=> true
    GuardedStruct.Info.sub_module(MyApp.User, :address) #=> MyApp.User.Address

# `authorized_fields?`

True if the section was declared with `authorized_fields: true`.

# `conditional_children`

Return the children variants of a `conditional_field`, or `nil` if the
name isn't a conditional. Each child is a meta map with `:kind`,
`:name`, and any associated options.

# `conditional_fields`

Names of all `conditional_field` entries on this module.

# `conditional_keys`

Names of conditional_field entries, sourced from `__information__/0`'s
`:conditional_keys` (matches `conditional_fields/1` for normal modules).

# `describe`

Return the FULL introspection map for a module: every section option,
every field's complete metadata, every derived flag, in one structure.

Works on both the top-level module and any generated sub_field submodule.
Section-option keys whose values were not declared are present as `nil`,
so the shape is uniform.

## Returned map keys

* `:module` — the module
* `:path` — module path from root (empty list for the top-level)
* `:key` — the field name corresponding to this module (or `:root`)
* `:shape` — `:struct` or `:pattern_map`
* `:pattern_keyed?` — convenience boolean
* `:patterns` — for pattern-map shapes, the list of regex field names
* `:keys` — struct-bound key names (excludes virtuals)
* `:enforce_keys` — names of enforced keys
* `:conditional_keys` — names of conditional_field entries
* `:options` — map of every section option (with `nil` for absent values)
* `:fields` — list of per-field meta maps (one per declared entity),
  each augmented with `:enforce?` (membership in enforce_keys) and,
  for sub_field entries, `:sub_module` (the generated submodule)

## Example

    Info.describe(MyApp.User)
    #=> %{
    #     module: MyApp.User,
    #     path: [],
    #     key: :root,
    #     shape: :struct,
    #     pattern_keyed?: false,
    #     patterns: [],
    #     keys: [:id, :name, :address, ...],
    #     enforce_keys: [:name, :address],
    #     conditional_keys: [:billing],
    #     options: %{
    #       enforce: true, opaque: false, module: nil, error: false,
    #       authorized_fields: true, main_validator: nil,
    #       validate_derive: nil, sanitize_derive: nil, json: true
    #     },
    #     fields: [
    #       %{name: :id, kind: :field, enforce?: false, ...},
    #       %{name: :address, kind: :sub_field, enforce?: true,
    #         sub_module: MyApp.User.Address, ...},
    #       ...
    #     ]
    #   }

# `dynamic?`

True if `name` is a `dynamic_field`.

# `dynamic_fields`

Names of all `dynamic_field` entries on this module.

# `enforce?`

True if the section was declared with `enforce: true`.

# `enforce?`

True if the field is enforced (member of `enforce_keys/0`).

# `enforce_keys`

Return the list of enforced field names.

# `error?`

True if the section was declared with `error: true`.

# `field`

Return the field metadata for a single name, or `nil` if absent. A
pattern-keyed module answers with the metadata of the first declared pattern
that matches the name.

# `field?`

True if the field exists on this module (or any sub_field cascade).

# `field_auto`

Return the `{Mod, fun}` `auto:` MFA, or `nil` if none.

# `field_default`

Return the field's `default:`, or `nil` if none or field absent.

# `field_derives`

Return the original derive string for a field (the canonical
`derives:` option, falling back to the legacy `derive:`).

# `field_kind`

Return the kind of a field: `:field`, `:sub_field`, `:virtual_field`,
`:dynamic_field`, `:conditional_field`, or `:pattern_field`. `nil` if
the field doesn't exist.

# `field_validator`

Return the `{Mod, fun}` per-field validator MFA, or `nil` if none.

# `fields`

Return the user-declared field, sub_field, virtual_field, dynamic_field
and conditional_field names in declaration order. Works on both the
top-level module and any generated sub_field submodule.

A pattern-keyed module has no names, so it lists its declared regex patterns.

# `fields_meta`

Return the runtime field metadata — same shape as the generated module's
`__fields__/0`.

# `guardedstruct`

```elixir
@spec guardedstruct(dsl_or_extended :: module() | map()) :: [struct()]
```

guardedstruct DSL entities

# `guardedstruct_auto_wire`

```elixir
@spec guardedstruct_auto_wire(dsl_or_extended :: module() | map()) ::
  {:ok, boolean()} | :error
```

Only effective inside the `GuardedStruct.AshResource` extension. When `true`, injects `GuardedStruct.AshResource.Change` into the resource's top-level `changes` section so every `:create` and `:update` action automatically runs the GuardedStruct pipeline. Equivalent to writing `changes do change GuardedStruct.AshResource.Change end` by hand. No-op outside the Ash extension.

# `guardedstruct_auto_wire!`

```elixir
@spec guardedstruct_auto_wire!(dsl_or_extended :: module() | map()) ::
  boolean() | no_return()
```

Only effective inside the `GuardedStruct.AshResource` extension. When `true`, injects `GuardedStruct.AshResource.Change` into the resource's top-level `changes` section so every `:create` and `:update` action automatically runs the GuardedStruct pipeline. Equivalent to writing `changes do change GuardedStruct.AshResource.Change end` by hand. No-op outside the Ash extension.

# `guardedstruct_json`

```elixir
@spec guardedstruct_json(dsl_or_extended :: module() | map()) ::
  {:ok, boolean()} | :error
```

When `true`, derives a JSON encoder. Uses `Jason.Encoder` if `:jason` is in the user's deps; otherwise falls back to the built-in `JSON.Encoder` on Elixir 1.18+. No-op if neither is available.

# `guardedstruct_json!`

```elixir
@spec guardedstruct_json!(dsl_or_extended :: module() | map()) ::
  boolean() | no_return()
```

When `true`, derives a JSON encoder. Uses `Jason.Encoder` if `:jason` is in the user's deps; otherwise falls back to the built-in `JSON.Encoder` on Elixir 1.18+. No-op if neither is available.

# `guardedstruct_main_validator`

```elixir
@spec guardedstruct_main_validator(dsl_or_extended :: module() | map()) ::
  {:ok, {atom(), atom()}} | :error
```

A `{Module, :function}` called with the attributes once every field has passed its validator; it returns `{:ok, attrs}` or `{:error, errors}`. When it is not set, a `main_validator/1` defined in the module is called instead.

# `guardedstruct_main_validator!`

```elixir
@spec guardedstruct_main_validator!(dsl_or_extended :: module() | map()) ::
  {atom(), atom()} | no_return()
```

A `{Module, :function}` called with the attributes once every field has passed its validator; it returns `{:ok, attrs}` or `{:error, errors}`. When it is not set, a `main_validator/1` defined in the module is called instead.

# `guardedstruct_options`

```elixir
@spec guardedstruct_options(dsl_or_extended :: module() | map()) :: %{
  required(atom()) =&gt; any()
}
```

guardedstruct DSL options

Returns a map containing the and any configured or default values.

# `guardedstruct_sanitize_derive`

```elixir
@spec guardedstruct_sanitize_derive(dsl_or_extended :: module() | map()) ::
  {:ok, atom() | [atom()]} | :error
```

A module, or a list of modules, whose `sanitize/2` serves the `sanitize(...)` ops the built-in ones and the derive extensions do not know. They are tried before the `:sanitize_derive` application config, which still serves the ops they do not handle.

# `guardedstruct_sanitize_derive!`

```elixir
@spec guardedstruct_sanitize_derive!(dsl_or_extended :: module() | map()) ::
  (atom() | [atom()]) | no_return()
```

A module, or a list of modules, whose `sanitize/2` serves the `sanitize(...)` ops the built-in ones and the derive extensions do not know. They are tried before the `:sanitize_derive` application config, which still serves the ops they do not handle.

# `guardedstruct_validate_derive`

```elixir
@spec guardedstruct_validate_derive(dsl_or_extended :: module() | map()) ::
  {:ok, atom() | [atom()]} | :error
```

A module, or a list of modules, whose `validate/3` serves the `validate(...)` ops the built-in ones and the derive extensions do not know. They are tried before the `:validate_derive` application config, which still serves the ops they do not handle.

# `guardedstruct_validate_derive!`

```elixir
@spec guardedstruct_validate_derive!(dsl_or_extended :: module() | map()) ::
  (atom() | [atom()]) | no_return()
```

A module, or a list of modules, whose `validate/3` serves the `validate(...)` ops the built-in ones and the derive extensions do not know. They are tried before the `:validate_derive` application config, which still serves the ops they do not handle.

# `json?`

True if the section was declared with `json: true`.

# `opaque?`

True if the section was declared with `opaque: true`.

# `pattern_keyed?`

True if this module was generated for a pattern-keyed map (its only
`field` was a regex). Pattern-keyed modules return a map from `builder/1`,
not a struct.

# `sub_fields`

Names of all `sub_field` entries on this module.

# `sub_module`

Return the generated submodule for a `sub_field`, or `nil` if the name
isn't a sub_field. The submodule path is the parent module concatenated
with the camelized field name (or with the section's `module:` override).

    Info.sub_module(MyApp.User, :address)
    #=> MyApp.User.Address

# `virtual?`

True if `name` is a `virtual_field`.

# `virtual_fields`

Names of all `virtual_field` entries on this module.

---

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