# `Linx.Netfilter.Patch`
[🔗](https://github.com/oshlabs/linx/blob/v0.2.0/lib/linx/netfilter/patch.ex#L1)

An ordered sequence of mutations that transforms one
`%Linx.Netfilter.Ruleset{}` into another.

Patches are produced by `Linx.Netfilter.diff/2` and consumed by
`Linx.Netfilter.push/2` (mode `:reconcile`) — together they
implement minimal-change updates: only the entities that actually
differ between current and desired state hit the wire.

## Ops

A patch is a list of `%Op{}` values. Op kinds:

  * `{:create_table, family, %Table{}}` — kernel doesn't have
    this table.
  * `{:delete_table, family, name}` — kernel has it, desired
    doesn't.
  * `{:create_chain, family, table_name, %Chain{}}`.
  * `{:delete_chain, family, table_name, chain_name}`.
  * `{:create_set, family, %Set{} | %Map{}}` — covers maps and
    vmaps too (dispatch on struct).
  * `{:delete_set, family, table_name, set_name}`.
  * `{:add_set_elements, family, table_name, set_name, [elem]}`.
  * `{:delete_set_elements, family, table_name, set_name, [elem]}`.
  * `{:create_rule, family, table_name, chain_name, %Rule{}, position}`
    — `position` is `:append` | `{:after, handle}` |
    `{:before, handle}` | `{:at_index, n}`.
  * `{:replace_rule, family, table_name, chain_name, handle, %Rule{}}`
    — in-place replace via `NLM_F_REPLACE`. Requires the
    kernel-assigned handle (carried by the rule pulled from
    the current ruleset).
  * `{:delete_rule, family, table_name, chain_name, handle}`.

## Ordering

Patch ops are topologically sorted so creates of dependencies
come before creates of dependents, and deletes happen in
reverse:

    1. delete rules
    2. delete set elements
    3. delete chains   (no orphan-rule reference issue)
    4. delete sets
    5. delete tables
    6. create tables
    7. create sets / maps
    8. create chains
    9. add set elements   (chain may be jump target)
   10. replace rules     (chains+sets already exist)
   11. create rules

This is the order `from_patch/1` emits inside one BATCH.

## Inspect

Renders compactly:

    #Linx.Netfilter.Patch<7 ops: 2 creates, 4 replaces, 1 delete>

# `op`

```elixir
@type op() ::
  {:create_table, atom(), Linx.Netfilter.Table.t()}
  | {:delete_table, atom(), String.t()}
  | {:create_chain, atom(), String.t(), Linx.Netfilter.Chain.t()}
  | {:delete_chain, atom(), String.t(), String.t()}
  | {:create_set, atom(), Linx.Netfilter.Set.t() | Linx.Netfilter.Map.t()}
  | {:delete_set, atom(), String.t(), String.t()}
  | {:add_set_elements, atom(), String.t(), String.t(), [term()]}
  | {:delete_set_elements, atom(), String.t(), String.t(), [term()]}
  | {:create_rule, atom(), String.t(), String.t(), Linx.Netfilter.Rule.t(),
     position()}
  | {:replace_rule, atom(), String.t(), String.t(), pos_integer(),
     Linx.Netfilter.Rule.t()}
  | {:delete_rule, atom(), String.t(), String.t(), pos_integer()}
```

# `position`

```elixir
@type position() ::
  :append
  | {:after, pos_integer()}
  | {:before, pos_integer()}
  | {:at_index, non_neg_integer()}
```

# `t`

```elixir
@type t() :: %Linx.Netfilter.Patch{ops: [op()]}
```

# `empty?`

```elixir
@spec empty?(t()) :: boolean()
```

Returns `true` iff the patch contains no ops.

# `new`

```elixir
@spec new([op()]) :: t()
```

An empty patch — same value as `diff(r, r)`.

# `sort`

```elixir
@spec sort(t()) :: t()
```

Topologically sorts a patch's ops into the canonical create/
delete order (see moduledoc). Idempotent — `sort/1` on an already-
sorted patch is a no-op.

---

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