# `Bandera.Store.Persistent.Ecto.Serializer`

Pure mapping between `Bandera.Gate`s and SQL table rows.

A row is a plain map `%{flag_name, gate_type, target, enabled}`. Both percentage
gate types collapse to `gate_type: "percentage"` (one percentage gate per flag),
with the ratio and kind encoded in `target` (`"time/<r>"` / `"actors/<r>"`). The
boolean gate's nil target is stored as the `"_bandera_none"` sentinel because SQL
unique indexes treat NULL values as distinct.

Flag names read back from storage are converted to atoms with `String.to_atom/1`
(so that listing flags created in a previous VM session works). Feature-flag
names must therefore be a bounded, developer-defined set — never untrusted user
input.

# `row`

```elixir
@type row() :: %{
  flag_name: String.t(),
  gate_type: String.t(),
  target: String.t(),
  enabled: boolean()
}
```

# `deserialize_flag`

```elixir
@spec deserialize_flag(atom() | String.t(), [map()]) :: Bandera.Flag.t()
```

Rebuilds a `Bandera.Flag` from the rows stored for it.

Rows are sorted for a stable gate order; the flag name is converted to an atom, so
it must be a bounded, developer-defined value — never untrusted input.

## Examples

    iex> rows = [%{gate_type: "boolean", target: "_bandera_none", enabled: true}]
    iex> flag = Bandera.Store.Persistent.Ecto.Serializer.deserialize_flag(:my_flag, rows)
    iex> flag.name
    :my_flag
    iex> flag.gates
    [%Bandera.Gate{type: :boolean, for: nil, enabled: true}]

# `serialize_target`

```elixir
@spec serialize_target(term()) :: String.t()
```

Encodes a gate target for the `target` column.

`nil` becomes the `"_bandera_none"` sentinel; binaries pass through; everything
else is stringified.

## Examples

    iex> Bandera.Store.Persistent.Ecto.Serializer.serialize_target(nil)
    "_bandera_none"

    iex> Bandera.Store.Persistent.Ecto.Serializer.serialize_target("user-1")
    "user-1"

    iex> Bandera.Store.Persistent.Ecto.Serializer.serialize_target(0.5)
    "0.5"

# `to_row`

```elixir
@spec to_row(atom(), Bandera.Gate.t()) :: row()
```

Maps a flag name and gate to the row map persisted by the Ecto adapter.

Percentage gates collapse to `gate_type: "percentage"` with the kind and ratio
encoded in `target`; a boolean gate's `nil` target becomes the `"_bandera_none"`
sentinel (SQL unique indexes treat `NULL`s as distinct).

## Examples

    iex> row = Bandera.Store.Persistent.Ecto.Serializer.to_row(:my_flag, Bandera.Gate.new(:boolean, true))
    iex> {row.flag_name, row.gate_type, row.target, row.enabled}
    {"my_flag", "boolean", "_bandera_none", true}

    iex> row = Bandera.Store.Persistent.Ecto.Serializer.to_row(:my_flag, Bandera.Gate.new(:actor, "u1", true))
    iex> {row.gate_type, row.target}
    {"actor", "u1"}

---

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