Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Safe strings to atoms, atoms to keys

safe_string_to_atom_keys.livemd

Safe strings to atoms, atoms to keys

Section

@doc """
Take a map with string keys and convert the keys to atoms, while converting keys to snake case.
Also converts listed enum values (represented as uppercase strings) to the corresponding atoms.
"""
@spec convert_string_keys_to_atom_keys(list(Map.t()) | Map.t(), list(String.t())) ::
        list(Map.t()) | Map.t()
def convert_string_keys_to_atom_keys(object, string_enum_values_to_convert \\ [])

def convert_string_keys_to_atom_keys(object, string_enum_values_to_convert)
    when is_map(object) do
  object
  |> Enum.reduce(%{}, fn {key, val}, acc ->
    # If key is already an atom, don't modify it
    new_key = if is_atom(key), do: key, else: key |> Macro.underscore() |> String.to_atom()

    Map.put(
      acc,
      new_key,
      convert_string_keys_to_atom_keys(val, string_enum_values_to_convert)
    )
  end)
end

def convert_string_keys_to_atom_keys(object, string_enum_values_to_convert)
    when is_list(object) do
  object
  |> Enum.map(fn current_object ->
    convert_string_keys_to_atom_keys(current_object, string_enum_values_to_convert)
  end)
end

def convert_string_keys_to_atom_keys(object, string_enum_values_to_convert)
    when is_binary(object) do
  if object in string_enum_values_to_convert do
    object |> String.downcase() |> Macro.underscore() |> String.to_atom()
  else
    object
  end
end

def convert_string_keys_to_atom_keys(object, _string_enum_values_to_convert) do
  object
end

@doc """
Take a map with atom keys and convert they keys to strings, while converting keys to camel case.
Also converts listed enum values (represented as atoms) to the corresponding uppercase strings.
"""
@spec convert_atom_keys_to_string_keys(list(Map.t()) | Map.t(), list(atom())) ::
        list(Map.t()) | Map.t()
def convert_atom_keys_to_string_keys(object, atom_enum_values_to_convert \\ [])

def convert_atom_keys_to_string_keys(object, atom_enum_values_to_convert) when is_map(object) do
  object
  |> Enum.reduce(%{}, fn {key, val}, acc ->
    # If key is already an string, don't modify it
    new_key = if is_binary(key), do: key, else: key |> Atom.to_string() |> Macro.underscore()

    Map.put(
      acc,
      new_key,
      convert_atom_keys_to_string_keys(val, atom_enum_values_to_convert)
    )
  end)
end

def convert_atom_keys_to_string_keys(object, atom_enum_values_to_convert)
    when is_list(object) do
  object
  |> Enum.map(fn current_object ->
    convert_atom_keys_to_string_keys(current_object, atom_enum_values_to_convert)
  end)
end

# Booleans are also atoms, we don't want to transform those
def convert_atom_keys_to_string_keys(object, atom_enum_values_to_convert)
    when is_atom(object) and not is_boolean(object) do
  if object in atom_enum_values_to_convert do
    Atom.to_string(object) |> String.upcase()
  else
    object
  end
end

def convert_atom_keys_to_string_keys(object, _atom_enum_values_to_convert) do
  object
end