# `Formata.Transform`
[🔗](https://github.com/gsmlg-dev/formata/blob/v0.1.0/lib/formata/transform.ex#L1)

Explicit, opt-in data transformations.

Encoders never drop, stringify, or replace values on their own. When a value
cannot be represented in a target format, call one of these functions (or pass
them to `Formata.convert/4` with `:transforms`) to make the loss explicit.

Every transformation returns `{:ok, data}` or `{:error, [%Formata.Error{kind: :transform}]}`.

    iex> Formata.Transform.drop_nils(%{"a" => 1, "b" => nil})
    {:ok, %{"a" => 1}}

    iex> Formata.Transform.temporal_to_iso8601(%{"on" => ~D[2024-05-01]})
    {:ok, %{"on" => "2024-05-01"}}

# `transform`

```elixir
@type transform() ::
  :drop_nils
  | :temporal_to_iso8601
  | :stringify_keys
  | :special_floats_to_strings
  | {:replace_nils, term()}
  | (term() -&gt; {:ok, term()} | {:error, [Formata.Error.t(), ...]})
```

# `apply_all`

```elixir
@spec apply_all(term(), [transform()]) ::
  {:ok, term()} | {:error, [Formata.Error.t(), ...]}
```

Applies a list of transformations in order, stopping at the first error.

Each element is a transformation name (`:drop_nils`, `:temporal_to_iso8601`,
`:stringify_keys`, `:special_floats_to_strings`), `{:replace_nils, value}`,
or a one-argument function returning `{:ok, data} | {:error, errors}`.

    iex> Formata.Transform.apply_all(%{"a" => nil, "t" => ~T[10:00:00]}, [:drop_nils, :temporal_to_iso8601])
    {:ok, %{"t" => "10:00:00"}}

# `drop_nils`

```elixir
@spec drop_nils(term()) :: {:ok, term()}
```

Removes map entries whose value is `nil`, recursively. `nil` list elements
are kept, because removing them would shift positions.

    iex> Formata.Transform.drop_nils(%{"a" => %{"b" => nil, "c" => [nil, 1]}})
    {:ok, %{"a" => %{"c" => [nil, 1]}}}

# `replace_nils`

```elixir
@spec replace_nils(term(), term()) :: {:ok, term()}
```

Replaces every `nil` (map values and list elements) with `value`.

    iex> Formata.Transform.replace_nils(%{"a" => nil, "b" => [nil]}, "")
    {:ok, %{"a" => "", "b" => [""]}}

# `special_floats_to_strings`

```elixir
@spec special_floats_to_strings(term()) :: {:ok, term()}
```

Converts `:infinity`, `:negative_infinity`, and `:nan` to the strings
`"inf"`, `"-inf"`, and `"nan"`, recursively.

    iex> Formata.Transform.special_floats_to_strings(%{"x" => :infinity})
    {:ok, %{"x" => "inf"}}

# `stringify_keys`

```elixir
@spec stringify_keys(term()) :: {:ok, term()} | {:error, [Formata.Error.t(), ...]}
```

Converts atom, integer, float, and boolean map keys to strings, recursively.

Fails with `:key_collision` if two keys of one map would become the same
string (for example `:a` and `"a"`), and with `:invalid_key` for keys that have
no string form (`nil`, tuples, lists, maps, ...).

    iex> Formata.Transform.stringify_keys(%{name: "Ada", tags: %{1 => true}})
    {:ok, %{"name" => "Ada", "tags" => %{"1" => true}}}

    iex> {:error, [%Formata.Error{code: :key_collision}]} =
    ...>   Formata.Transform.stringify_keys(%{:a => 1, "a" => 2})

# `temporal_to_iso8601`

```elixir
@spec temporal_to_iso8601(term()) :: {:ok, term()}
```

Converts `Date`, `Time`, `NaiveDateTime`, and `DateTime` values to ISO 8601
strings, recursively.

    iex> Formata.Transform.temporal_to_iso8601([~U[2024-01-02 03:04:05Z]])
    {:ok, ["2024-01-02T03:04:05Z"]}

---

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