lib/step_flow/map_tool.ex

defmodule StepFlow.Map do
  @moduledoc """
  Extend Map with some additional functions.
  """

  @doc """
  Get a key matching on an atom or a string.

  Default value can be specified.

  ## Examples

      iex> StepFlow.Map.get_by_key_or_atom(%{key: "value"}, :key)
      "value"

      iex> StepFlow.Map.get_by_key_or_atom(%{"key" => "value"}, :key)
      "value"

      iex> StepFlow.Map.get_by_key_or_atom(%{key: "value"}, "key")
      "value"

      iex> StepFlow.Map.get_by_key_or_atom(%{"key" => "value"}, "key")
      "value"

  """
  def get_by_key_or_atom(dict, atom, default \\ nil)

  def get_by_key_or_atom(dict, atom, default) when is_atom(atom) do
    Map.get_lazy(dict, atom, fn -> Map.get(dict, Atom.to_string(atom), default) end)
  end

  def get_by_key_or_atom(dict, string, default) when is_bitstring(string) do
    Map.get_lazy(dict, string, fn -> Map.get(dict, String.to_atom(string), default) end)
  end

  def get_by_key_or_atom(_, _, _) do
    raise "Got unsupported key type instead of expected Atom or String."
  end

  @doc """
  Replace an item in a map, with atom or string keys.

  ## Examples

      iex> StepFlow.Map.replace_by_atom(%{key: "value"}, :key, "replaced_value")
      %{key: "replaced_value"}

      iex> StepFlow.Map.replace_by_atom(%{"key" => "value"}, :key, "replaced_value")
      %{key: "replaced_value"}

      iex> StepFlow.Map.replace_by_atom(%{"key" => "value"}, "key", "replaced_value")
      %{key: "replaced_value"}

  """
  def replace_by_atom(dict, atom, value) when is_atom(atom) do
    dict
    |> Map.delete(Atom.to_string(atom))
    |> Map.delete(atom)
    |> Map.put(atom, value)
  end

  def replace_by_atom(dict, string, value) when is_bitstring(string) do
    dict
    |> Map.delete(String.to_atom(string))
    |> Map.delete(string)
    |> Map.put(String.to_atom(string), value)
  end

  def replace_by_atom(_dict, _atom, _value) do
    raise "Got unsupported key type instead of expected Atom or String."
  end

  def replace_by_string(dict, string, value) when is_bitstring(string) do
    dict
    |> Map.delete(String.to_atom(string))
    |> Map.delete(string)
    |> Map.put(string, value)
  end

  def replace_by_string(_dict, _string, _value) do
    raise "Got unsupported 2nd argument type, expected String."
  end
end