Current section
Files
Jump to
Current section
Files
lib/sat/certificados/map_keys.ex
defmodule Sat.Certificados.MapKeys do
@moduledoc false
# Helper interno: transforma recursivamente las llaves de un mapa al tipo
# elegido (`:atom`, `:string`, `:existing`).
#
# Comparte la semántica con `CFDI.to_map/2` para mantener coherencia entre
# paquetes.
#
# * `:atom` — `String.to_atom/1`. Peligroso con keys arbitrarias
# (la atom table no tiene GC).
# * `:string` — siempre string.
# * `:existing` — `String.to_existing_atom/1`; fallback a string si el
# átomo no existe en la VM. Seguro.
@spec transform(map() | list() | term(), :atom | :string | :existing) :: term()
def transform(value, mode), do: do_transform(value, key_fn(mode))
defp do_transform(map, fun) when is_map(map) and not is_struct(map) do
Map.new(map, fn {k, v} -> {fun.(k), do_transform(v, fun)} end)
end
defp do_transform(list, fun) when is_list(list), do: Enum.map(list, &do_transform(&1, fun))
defp do_transform(other, _fun), do: other
defp key_fn(:atom), do: &atomize/1
defp key_fn(:string), do: &stringify/1
defp key_fn(:existing), do: &existing_atom/1
defp key_fn(other),
do:
raise(
ArgumentError,
"opción :keys inválida: #{inspect(other)}; usar :atom, :string o :existing"
)
defp atomize(k) when is_atom(k), do: k
defp atomize(k) when is_binary(k), do: String.to_atom(k)
defp stringify(k) when is_atom(k), do: Atom.to_string(k)
defp stringify(k) when is_binary(k), do: k
defp existing_atom(k) when is_atom(k), do: k
defp existing_atom(k) when is_binary(k) do
String.to_existing_atom(k)
rescue
ArgumentError -> k
end
end