Packages
rockbox_ex
0.1.0
Idiomatic Elixir SDK for Rockbox — pipe-friendly, builder-friendly, with real-time event subscriptions and a plugin system.
Current section
Files
Jump to
Current section
Files
lib/rockbox/util.ex
defmodule Rockbox.Util do
@moduledoc false
# Internal helpers — JSON key conversion between the GraphQL camelCase wire
# format and idiomatic snake_case Elixir keys, plus typed enum coercion.
@doc "Recursively convert string camelCase map keys to snake_case atoms."
@spec atomize(term()) :: term()
def atomize(value) when is_list(value), do: Enum.map(value, &atomize/1)
def atomize(%{} = map) do
Map.new(map, fn {k, v} -> {to_snake_atom(k), atomize(v)} end)
end
def atomize(other), do: other
@doc "Recursively convert snake_case atom map keys to camelCase strings."
@spec camelize(term()) :: term()
def camelize(value) when is_list(value), do: Enum.map(value, &camelize/1)
def camelize(%{} = map) do
Map.new(map, fn {k, v} -> {to_camel_string(k), camelize(v)} end)
end
def camelize(other), do: other
@doc "Build a struct from a (possibly camelCase) map, ignoring unknown keys."
@spec to_struct(module(), map() | nil) :: struct() | nil
def to_struct(_module, nil), do: nil
def to_struct(module, %{} = map) do
atomized = atomize(map)
blank = struct(module)
fields = blank |> Map.from_struct() |> Map.keys()
relevant = Map.take(atomized, fields)
struct(module, relevant)
end
@doc "Build a list of structs from a list of maps."
@spec to_struct_list(module(), [map()] | nil) :: [struct()]
def to_struct_list(_module, nil), do: []
def to_struct_list(module, list), do: Enum.map(list, &to_struct(module, &1))
@doc "Convert a keyword list of options into a camelCase variables map for GraphQL."
@spec opts_to_variables(Keyword.t() | map()) :: map()
def opts_to_variables(opts) when is_list(opts), do: opts |> Map.new() |> camelize()
def opts_to_variables(opts) when is_map(opts), do: camelize(opts)
# ---------------------------------------------------------------------------
# Internal
# ---------------------------------------------------------------------------
defp to_snake_atom(key) when is_atom(key), do: key
defp to_snake_atom(key) when is_binary(key) do
key
|> String.replace(~r/([a-z0-9])([A-Z])/, "\\1_\\2")
|> String.downcase()
|> String.to_atom()
end
defp to_camel_string(key) when is_binary(key), do: to_camel_string(String.to_atom(key))
defp to_camel_string(key) when is_atom(key) do
[head | rest] = key |> Atom.to_string() |> String.split("_")
head <> Enum.map_join(rest, "", &String.capitalize/1)
end
end