Current section

Files

Jump to
phoenix_kit_emails lib phoenix_kit modules emails utils json.ex
Raw

lib/phoenix_kit/modules/emails/utils/json.ex

defmodule PhoenixKit.Modules.Emails.Utils.Json do
@moduledoc """
Thin helper around Elixir's built-in `JSON` module (1.18+) for the one
thing it doesn't provide: pretty-printing.
This module's own code uses the built-in `JSON` module directly for plain
encode/decode — this exists only for the handful of call sites (JSON
offered to an admin as a downloadable export) that need indented output.
Mirrors `PhoenixKit.Utils.Json` from core (phoenix_kit) — duplicated here
because that helper isn't in the currently-published core version this
project depends on. Safe to delete and delegate to core's version once a
release with it is available.
"""
@doc """
Encodes `term` as indented, human-readable JSON (2-space indent).
`term` is encoded via `JSON.encode!/1` and decoded back first, so structs
(e.g. Ecto schemas with `@derive {JSON.Encoder, ...}`) go through the
`JSON.Encoder` protocol correctly — the recursive pretty-printer below
only ever walks plain maps/lists/primitives, never a raw struct (which
doesn't implement `Enumerable`).
## Examples
iex> PhoenixKit.Modules.Emails.Utils.Json.encode_pretty!(%{"a" => 1})
~s({\\n "a": 1\\n})
"""
@spec encode_pretty!(term()) :: String.t()
def encode_pretty!(term) do
term
|> JSON.encode!()
|> JSON.decode!()
|> pretty(0)
|> IO.iodata_to_binary()
end
defp pretty(map, _indent) when is_map(map) and map_size(map) == 0, do: "{}"
defp pretty(map, indent) when is_map(map) do
inner_indent = indent + 2
entries =
map
|> Enum.map(fn {key, value} ->
[indent_of(inner_indent), JSON.encode!(to_string(key)), ": ", pretty(value, inner_indent)]
end)
|> Enum.intersperse(",\n")
["{\n", entries, "\n", indent_of(indent), "}"]
end
defp pretty([], _indent), do: "[]"
defp pretty(list, indent) when is_list(list) do
inner_indent = indent + 2
entries =
list
|> Enum.map(fn value -> [indent_of(inner_indent), pretty(value, inner_indent)] end)
|> Enum.intersperse(",\n")
["[\n", entries, "\n", indent_of(indent), "]"]
end
defp pretty(value, _indent), do: JSON.encode!(value)
defp indent_of(n), do: String.duplicate(" ", n)
end