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

Runtime helpers for `~NFT` sigils that contain `#{...}`
interpolations.

When the sigil body has no interpolations, `Linx.NFT.Compiler`
produces a `%Linx.Netfilter.Ruleset{}` at compile time and the
macro emits it as a literal. When there are interpolations, the
static value isn't knowable until runtime, so
`Linx.NFT.RuntimeCompiler` emits Elixir code that constructs the
Ruleset at runtime. At each `#{expr}` position, it calls into
one of the helpers here — they take an evaluated Elixir value
plus the **field kind** the surrounding nft syntax expects
(`{:int, 1|2|4|8}`, `:ipv4`, `:ipv6`, `:ifname`), validate, and
return either an encoded `%Expr{}` ready to splice into the
rule's expression list or the raw bytes to use as a comparison
value.

Errors at runtime raise `ArgumentError` with a message naming
the expected kind and the actual value — there's no
source-location context here (the macro keeps the AST node's
meta on the static side, but the runtime helper just sees the
value), so test the interpolated values close to the sigil
call site.

## Supported value kinds

  * `{:int, width}` — `width` ∈ `1, 2, 4, 8` bytes, big-endian
    encoded. Accepts any non-negative integer that fits.
  * `:ipv4` — `Linx.IP.parse/1`-able string, 4-tuple
    `{a, b, c, d}`, raw 4-byte binary, or `%Linx.IP{family:
    :inet}`. Returns 4 bytes.
  * `:ipv6` — same shape extended: parse-able string, 8-tuple
    of `0..0xFFFF`, raw 16-byte binary, or `%Linx.IP{family:
    :inet6}`. Returns 16 bytes.
  * `:ifname` — binary, padded/truncated to IFNAMSIZ (16
    bytes) with trailing zero bytes.

Each kind has a matching `encode_*!/1` helper that returns the
raw bytes, plus the convenience `cmp!/3` that wraps the bytes
in an `%Expr{name: :cmp}` ready to splice into a rule.

# `cmp!`

```elixir
@spec cmp!(atom(), term(), {:int, 1 | 2 | 4 | 8} | :ipv4 | :ipv6 | :ifname) ::
  Linx.Netfilter.Expr.t()
```

Encodes `value` for the given `kind`, then builds an
`%Expr{name: :cmp}` with `op`. Returns the `%Expr{}`. Raises
`ArgumentError` if `value` doesn't match the expected kind.

# `encode!`

```elixir
@spec encode!(term(), {:int, 1 | 2 | 4 | 8} | :ipv4 | :ipv6 | :ifname) :: binary()
```

Encodes `value` to a binary suitable for the given kind. Raises
`ArgumentError` on type mismatch / out-of-range.

# `encode_ifname!`

```elixir
@spec encode_ifname!(binary()) :: &lt;&lt;_::128&gt;&gt;
```

Pads an interface-name binary out to IFNAMSIZ (16 bytes) with
trailing zeros. Truncates if longer (matches kernel behaviour —
the trailing bytes are ignored).

# `encode_int!`

```elixir
@spec encode_int!(integer(), 1 | 2 | 4 | 8) :: binary()
```

Encodes an integer to a `width`-byte big-endian binary.

# `encode_ipv4!`

```elixir
@spec encode_ipv4!(term()) :: &lt;&lt;_::32&gt;&gt;
```

Encodes an IPv4 input to its 4-byte big-endian form.

# `encode_ipv6!`

```elixir
@spec encode_ipv6!(term()) :: &lt;&lt;_::128&gt;&gt;
```

Encodes an IPv6 input to its 16-byte big-endian form.

---

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