Packages
Multi-surface application runtime for Elixir. One TEA module renders to terminal, browser (LiveView), SSH, and MCP (agents). 30+ widgets, flexbox + CSS grid, AI agent runtime, distributed swarm with CRDTs, time-travel debugging, session recording, sandboxed REPL, and agentic commerce.
Current section
Files
Jump to
Current section
Files
lib/raxol/utils/map_utils.ex
defmodule Raxol.Utils.MapUtils do
@moduledoc """
Common utility functions for map operations.
This module consolidates frequently used map transformation functions
to avoid code duplication across the codebase.
"""
@doc """
Recursively converts all map keys to strings.
## Examples
iex> Raxol.Utils.MapUtils.stringify_keys(%{foo: "bar", nested: %{key: "value"}})
%{"foo" => "bar", "nested" => %{"key" => "value"}}
iex> Raxol.Utils.MapUtils.stringify_keys(%{:atom => [%{inner: "value"}]})
%{"atom" => [%{"inner" => "value"}]}
"""
@spec stringify_keys(any()) :: any()
def stringify_keys(map) when is_map(map) do
Enum.reduce(map, %{}, fn {key, value}, acc ->
string_key = to_string(key)
stringified_value =
case value do
v when is_map(v) -> stringify_keys(v)
v when is_list(v) -> Enum.map(v, &stringify_keys/1)
v -> v
end
Map.put(acc, string_key, stringified_value)
end)
end
def stringify_keys(value), do: value
@doc """
Recursively converts all map keys to atoms.
## Examples
iex> Raxol.Utils.MapUtils.atomize_keys(%{"foo" => "bar", "nested" => %{"key" => "value"}})
%{foo: "bar", nested: %{key: "value"}}
iex> Raxol.Utils.MapUtils.atomize_keys(%{"atom" => [%{"inner" => "value"}]})
%{atom: [%{inner: "value"}]}
"""
@spec atomize_keys(any()) :: any()
def atomize_keys(map) when is_map(map) do
Enum.reduce(map, %{}, fn {key, value}, acc ->
atom_key =
cond do
is_atom(key) -> key
is_binary(key) -> String.to_atom(key)
true -> String.to_atom(to_string(key))
end
atomized_value =
case value do
v when is_map(v) -> atomize_keys(v)
v when is_list(v) -> Enum.map(v, &atomize_keys/1)
v -> v
end
Map.put(acc, atom_key, atomized_value)
end)
end
def atomize_keys(value), do: value
@doc """
Safely atomizes keys, only converting strings that already exist as atoms.
This prevents atom exhaustion attacks.
## Examples
iex> Raxol.Utils.MapUtils.safe_atomize_keys(%{"foo" => "bar"})
%{"foo" => "bar"} # "foo" atom doesn't exist
iex> _ = :existing_atom
iex> Raxol.Utils.MapUtils.safe_atomize_keys(%{"existing_atom" => "value"})
%{existing_atom: "value"}
"""
@spec safe_atomize_keys(any()) :: any()
def safe_atomize_keys(map) when is_map(map) do
Enum.reduce(map, %{}, fn {key, value}, acc ->
safe_key =
cond do
is_atom(key) -> key
is_binary(key) -> String.to_existing_atom(key)
true -> key
end
|> case do
key when is_atom(key) -> key
_ -> key
end
safe_value =
case value do
v when is_map(v) -> safe_atomize_keys(v)
v when is_list(v) -> Enum.map(v, &safe_atomize_keys/1)
v -> v
end
Map.put(acc, safe_key, safe_value)
end)
rescue
ArgumentError -> map
end
def safe_atomize_keys(value), do: value
end