# `Localize.Message.Validator`
[🔗](https://github.com/elixir-localize/localize/blob/v1.2.0/lib/localize/message/validator.ex#L1)

Validates a parsed MF2 message against the data-model rules of
TR35 Part 9, "Data Model Errors".

`Localize.Message.Parser.parse/1` checks *syntax* only, so a
syntactically valid message can still be semantically invalid — most
commonly a `.local` declaration that reads the variable it declares.
Parsing and validation are kept separate so tooling that must accept
invalid input, such as a syntax highlighter, can parse without
validating; everything that formats or serializes a message runs both:

    with {:ok, ast} <- Localize.Message.Parser.parse(message),
         :ok <- Localize.Message.Validator.validate(ast) do
      # syntax errors surface first, then data-model errors
    end

# `error`

```elixir
@type error() ::
  {:duplicate_declaration, String.t()}
  | {:duplicate_option_name, String.t()}
  | {:duplicate_variant, String.t()}
  | {:missing_selector_annotation, String.t()}
  | {:variant_key_mismatch, String.t()}
  | {:missing_fallback_variant, String.t()}
```

# `validate`

```elixir
@spec validate(term()) :: :ok | {:error, error()}
```

Validates a parsed MF2 message against the TR35 data-model rules.

The rules checked are:

* **Duplicate Declaration** — a variable declared more than once,
  including declaring a variable after it was implicitly declared by
  use in an earlier declaration, and a `.local` referring to itself.

* **Duplicate Option Name** — the same identifier on the left of more
  than one option in a single expression or markup.

* **Duplicate Variant** — the same key list (after NFC normalization
  of literal keys) on more than one variant.

* **Missing Selector Annotation** — a selector that does not resolve,
  directly or transitively, to a declaration carrying a function.

* **Variant Key Mismatch** — a variant whose key count differs from
  the number of selectors.

* **Missing Fallback Variant** — a matcher with no variant whose keys
  are all `*`.

### Arguments

* `ast` is a parsed message as returned by
  `Localize.Message.Parser.parse/1`.

### Returns

* `:ok` if the message satisfies every data-model rule.

* `{:error, {reason, detail}}` for the first violation found, where
  `detail` names the offending variable, option, or key list.

### Examples

    iex> {:ok, ast} = Localize.Message.Parser.parse(".input {$n :number}\n.match $n\n* {{ok}}")
    iex> Localize.Message.Validator.validate(ast)
    :ok

    iex> {:ok, ast} = Localize.Message.Parser.parse(".local $n = {$n :number}\n.match $n\n* {{no}}")
    iex> Localize.Message.Validator.validate(ast)
    {:error, {:duplicate_declaration, "n"}}

---

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