# `Mutare.Mutator.Families`
[🔗](https://github.com/foxbenjaminfox/mutare/blob/v0.2.1/lib/mutare/mutator/families.ex#L1)

A declarative family catalog for a family-rich mutator's configuration.

A plugin that emits many mutation kinds under one mutator module usually exposes a
`families:` option so users can narrow the catalog. This module generates that option's
selection-and-validation machinery from one declaration, so every such plugin parses the
same grammar — the one Mutare's own `{:builtins, except: […]}` uses — and fails loudly in
the same shapes:

    defmodule MyPlugin.Config do
      use Mutare.Mutator.Families,
        plugin: "MyPlugin",
        all: ~w(comparison connective null_predicate string_literal)a,
        opt_in: ~w(string_literal)a
    end

`use` options:

  * `:all` (required) — every family the mutator can emit, ordered; duplicates rejected.
  * `:opt_in` (default `[]`) — the subset excluded from the default set; each must be
    in `:all`.
  * `:plugin` (default the using module's name) — the plugin name included in error messages,
    e.g. `"Mutare.Ecto"`.

The declaration generates, all overridable:

  * `all_families/0` — the `:all` list;
  * `default_families/0` — `all -- opt_in`, the set used when `families:` is unset;
  * `parse_families!/1` — a configured `families:` value (`:default`, `:all`, an explicit
    list, or `{:all | :default, except: […]}`) to the enabled `MapSet`, raising
    `ArgumentError` on an unknown family or a malformed `except:`;
  * `family_enabled?/2` — whether a family is in a parsed set, or in the set a raw
    keyword list's `families:` value parses to;
  * `@type family` — the union of the declared family atoms.

Only selection and validation are generated. What each family *means* — notes, variant
labels, delivery — stays the plugin's, typically alongside this `use` in its config module.
Parse once per run by calling `parse_families!/1` from the mutator's
`c:Mutare.Mutator.init/1` and reading the result back from `context.config`; then *apply*
the selection once, in `c:Mutare.Mutator.finalize/2` — Mutare runs it on every produced
mutation, on both delivery paths, so producers stay pure and every delivery site applies
the filter.

# `catalog`

```elixir
@type catalog() :: %{plugin: String.t(), all: [atom()], default: [atom()]}
```

The catalog a `use Mutare.Mutator.Families` declaration compiles to — the value the
generated functions close over and the runtime faces (`parse!/2`, `enabled?/3`) take.

# `enabled?`

```elixir
@spec enabled?(MapSet.t(atom()) | keyword(), atom(), catalog()) :: boolean()
```

Whether `family` is enabled — the runtime behind the generated `family_enabled?/2`.
A `MapSet` is an already-parsed selection; a keyword list is raw options, whose
`families:` value (default `:default`) is parsed first.

# `except!`

```elixir
@spec except!([atom()], term(), catalog(), String.t()) :: [atom()]
```

`base` minus a validated `except:` list — the one parser behind `{:builtins, except: […]}`
and `{:all | :default, except: […]}`, so the grammar and its failure shapes are owned once.

`opts` must be a keyword list whose only key is `:except`, naming families in `catalog.all`,
so a typo (`{:builtins, exept: …}` / `except: [:integr_literal]`) fails loudly rather than
silently keeping a family it meant to drop. `token` names the form in the error messages
(`":builtins"`, `"MyPlugin families {:all | :default, ...}"`).

# `parse!`

```elixir
@spec parse!(term(), catalog()) :: MapSet.t(atom())
```

Parses a `families:` value against `catalog` — the runtime behind the generated
`parse_families!/1`, kept here so the grammar and its error messages are owned once.

---

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