Linx.NFT.Formatter (Linx v0.1.0)

Copy Markdown View Source

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 + cmptcp dport 22, payload + bitwise + cmp → CIDR, payload + lookuptcp dport @ports, payload + __anon_settcp dport { 22, 80 }, ct + cmpct state established, meta + cmpmeta 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.)

Summary

Functions

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

Functions

format(ruleset)

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

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