# `Linx.NFT.Formatter`
[🔗](https://github.com/oshlabs/linx/blob/v0.2.0/lib/linx/nft/formatter.ex#L1)

Canonical-emit pretty-printer for `%Linx.Netfilter.Ruleset{}`.

The inverse of `Linx.NFT.Compiler`: walks the ruleset value and
emits syntactically valid nft source. The output round-trips
back through `Linx.NFT.parse/1` to a structurally equivalent
ruleset (modulo trivia — `format/1` makes no attempt to preserve
the comments, blank lines, or original ordering of items it
wasn't itself told about). Trivia-preserving emit is a v2
enhancement; this commit's goal is canonicalisation, not source
fidelity.

## Per-construct policy

  * Tables are emitted in `{family, name}` order.
  * Inside each table: chains first, then sets, then maps and
    vmaps. (Element-order across these blocks is independent of
    original source.)
  * Chains emit the full base header (`type hook priority`) on
    one line, then `policy X` on the next (if set), a blank
    line, then one rule per line.
  * Rules emit as a single space-joined statement sequence,
    optionally trailed by `comment "…"`.
  * Expressions are paired into match statements (`payload +
    cmp` → `tcp dport 22`, `payload + bitwise + cmp` → CIDR,
    `payload + lookup` → `tcp dport @ports`, `payload +
    __anon_set` → `tcp dport { 22, 80 }`, `ct + cmp` →
    `ct state established`, `meta + cmp` → `meta iif "eth0"`).
    Standalone expressions (`counter`, `log`, `reject`, NAT,
    etc.) emit as their token form.

## Limitations

Anything the formatter doesn't yet know how to render emits a
`# <unsupported expression: …>` comment in line, so the output
remains valid nft (a comment) and the gap is visible. As the
compiler grows (e.g. `limit`, `meta mark set`), the formatter
gains the inverse cases alongside.

## `mix format` integration

Implements the `Mix.Tasks.Format` behaviour: when listed under
`:plugins` in a project's `.formatter.exs`, `mix format`
reflows both inline `~NFT"…"` sigil bodies inside `.ex`
sources AND standalone `.nft` files. Users wire it up with:

    # .formatter.exs
    [
      plugins: [Linx.NFT.Formatter],
      inputs: ["{lib,test}/**/*.{ex,exs}", "**/*.nft"]
    ]

Behaviour:

  * **Static `~NFT` sigil body / `.nft` file** — parses,
    runs through `format/1`, returns the canonical
    single-formatted source. Idempotent.
  * **Interpolation-bearing `~NFT` sigil body** — left
    verbatim. AST-aware formatting that preserves `#{…}`
    positions while reflowing the surrounding nft syntax is a
    future enhancement.
  * **Parse error in a `.nft` file** — raises
    `Linx.NFT.ParseError`, surfacing visibly so the user
    fixes the bad file. (For sigils, parse errors leave the
    body unchanged — the surrounding compile run will report
    the same error anyway, with better stack context.)

# `format`

```elixir
@spec format(Linx.Netfilter.Ruleset.t()) :: String.t()
```

Emits `%Ruleset{}` as nft source. Always returns a binary; never
raises.

---

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