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

Field-level diffs between two GuardedStruct instances.

    iex> GuardedStruct.Diff.diff(
    ...>   %User{name: "Alice", age: 30, role: "admin"},
    ...>   %User{name: "Alice", age: 31, role: "user"}
    ...> )
    %{
      age: {:changed, 30, 31},
      role: {:changed, "admin", "user"}
    }

Equal fields are omitted. Nested struct fields recurse — `name: %User{} → %User{}`
produces a nested map, not a `:changed` tuple.

Useful for audit logs, CRM history, and "what changed" UIs.

# `apply`

```elixir
@spec apply(struct() | map(), map()) :: struct() | map()
```

Apply a diff to a struct, returning the result of applying the new values.

    iex> User.builder(%{name: "Alice", age: 30}) |> elem(1) |> GuardedStruct.Diff.apply(%{age: {:changed, 30, 31}})
    %User{name: "Alice", age: 31}

Nested struct diffs apply recursively. `:changed` tuples replace the field
with their `new` value. Unknown keys in the diff are silently ignored.

# `diff`

```elixir
@spec diff(struct() | map() | any(), struct() | map() | any()) ::
  map() | :not_comparable
```

Diff two structs of the same module. Returns a map keyed by field name with
values of one of:

  * `{:changed, old, new}` — primitive value differs
  * `%{...}` — nested struct, recursively diffed (only if there ARE changes)

Equal fields are omitted from the result.

Returns `:not_comparable` if the two values are not the same struct module.

# `equal?`

```elixir
@spec equal?(any(), any()) :: boolean()
```

Returns `true` if the two structs are equal field-by-field (same as
`diff(a, b) == %{}` but skips work for unchanged fields).

---

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