# `AshGrant.Explanation`
[🔗](https://github.com/jhlee111/ash_grant/blob/v0.14.1/lib/ash_grant/explanation.ex#L1)

Represents a detailed explanation of an authorization decision.

This struct is returned by `AshGrant.explain/4` and provides comprehensive
information about why an authorization check succeeded or failed.

## Fields

- `:resource` - The Ash resource being checked
- `:action` - The action being checked (e.g., `:read`, `:update`)
- `:actor` - The actor performing the action
- `:context` - Optional context passed to the check
- `:decision` - The final decision (`:allow` or `:deny`)
- `:reason` - Reason for denial (`:no_matching_permissions`, `:denied_by_rule`, etc.)
- `:matching_permissions` - List of permissions that matched and granted access
- `:evaluated_permissions` - All permissions that were evaluated with match status
- `:scope_filter` - The resolved scope filter expression (for reads)
- `:field_groups` - List of field group names the actor has access to (from 5-part permissions)
- `:field_group_defs` - Field group definitions from the resource DSL
- `:resolve_arguments` - `resolve_argument` declarations active for this action, each annotated with the scope atoms that trigger the resolver at runtime (see `guides/argument-based-scope.md`)

## Example

    iex> result = AshGrant.explain(MyApp.Post, :read, actor)
    %AshGrant.Explanation{
      resource: MyApp.Post,
      action: :read,
      decision: :allow,
      matching_permissions: [
        %{
          permission: "post:*:read:always",
          description: "Read all posts",
          source: "editor_role",
          scope_name: :all,
          scope_description: "All records without restriction",
          field_group: nil
        }
      ],
      field_groups: [],
      field_group_defs: [],
      ...
    }

    iex> AshGrant.Explanation.to_string(result)
    """
    ═══════════════════════════════════════════════════════════════════
    Authorization Explanation for MyApp.Post
    ═══════════════════════════════════════════════════════════════════
    Action:   read
    Decision: ✓ ALLOW
    ...
    """

# `evaluated_permission`

```elixir
@type evaluated_permission() :: %{
  permission: String.t(),
  matched: boolean(),
  reason: String.t() | nil,
  description: String.t() | nil,
  source: String.t() | nil,
  scope_name: atom() | nil,
  scope_description: String.t() | nil,
  field_group: String.t() | nil
}
```

# `resolve_argument_entry`

```elixir
@type resolve_argument_entry() :: %{
  name: atom(),
  from_path: [atom()],
  for_actions: [atom()] | nil,
  scopes_needing: [atom()]
}
```

# `t`

```elixir
@type t() :: %AshGrant.Explanation{
  action: atom(),
  actor: term(),
  context: map() | nil,
  decision: :allow | :deny,
  evaluated_permissions: [evaluated_permission()],
  field_group_defs: [AshGrant.Dsl.FieldGroup.t()],
  field_groups: [String.t()],
  matching_permissions: [evaluated_permission()],
  reason: atom() | nil,
  resolve_arguments: [resolve_argument_entry()],
  resource: module(),
  scope_filter: term() | nil
}
```

# `to_string`

```elixir
@spec to_string(
  t(),
  keyword()
) :: String.t()
```

Converts an Explanation struct to a human-readable string.

## Options

- `:color` - Whether to include ANSI color codes (default: `true`)
- `:verbose` - Whether to include all evaluated permissions (default: `false`)

## Example

    iex> result = AshGrant.explain(MyApp.Post, :read, actor)
    iex> IO.puts(AshGrant.Explanation.to_string(result))
    ═══════════════════════════════════════════════════════════════════
    Authorization Explanation for MyApp.Post
    ═══════════════════════════════════════════════════════════════════
    Action:   read
    Decision: ✓ ALLOW
    ...

---

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