defmodule Chaperon.Util do @moduledoc """ Helper functions used throughout `Chaperon`'s codebase. """ @spec preserve_vals_merge(map, map) :: map def preserve_vals_merge(map1, map2) do new_map = for {k, v2} <- map2 do case map1[k] do nil -> {k, v2} v1 when is_list(v1) and is_list(v2) -> {k, v2 ++ v1} v1 when is_list(v1) -> {k, [v2 | v1]} v1 -> {k, [v2, v1]} end end |> Enum.into(%{}) map1 |> Map.merge(new_map) end @doc """ Converts a map's values to be prefixed (put in a tuple as the first element). ## Examples iex> Chaperon.Util.map_prefix_value(%{foo: 1, bar: 2}, :wat) %{foo: {:wat, 1}, bar: {:wat, 2}} """ @spec map_prefix_value(map, any) :: map def map_prefix_value(map, prefix) do for {k, v} <- map do {k, {prefix, v}} end |> Enum.into(%{}) end @doc """ Inserts a given key-value pair (`{k2, v2}` under any values within `map` that are also maps). ## Example iex> m = %{a: 1, b: %{baz: 3}, c: %{foo: 1, bar: 2}} iex> Chaperon.Util.map_nested_put(m, :baz, 10) %{a: 1, b: %{baz: 10}, c: %{foo: 1, bar: 2, baz: 10}} iex> Chaperon.Util.map_nested_put(m, :foo, "ok") %{a: 1, b: %{baz: 3, foo: "ok"}, c: %{foo: "ok", bar: 2}} """ @spec map_nested_put(map, any, any) :: map def map_nested_put(map, k2, v2) do for {k, v} <- map do case v do v when is_map(v) -> {k, Map.put(v, k2, v2)} v -> {k, v} end end |> Enum.into(%{}) end @doc """ Returns last `amount` elements in a given `Enum` as a `List`. ## Example iex> alias Chaperon.Util iex> [] |> Util.last(1) [] iex> [1] |> Util.last(1) [1] iex> [1,2,3,4] |> Util.last(1) [4] iex> [1,2,3,4] |> Util.last(2) [3,4] iex> [1,2,3,4] |> Util.last(3) [2,3,4] iex> [1,2,3,4] |> Util.last(4) [1,2,3,4] iex> [1,2,3,4] |> Util.last(5) [1,2,3,4] """ def last(enum, amount) when is_list(enum) do case Enum.count(enum) - amount do n when n > 0 -> enum |> Enum.drop(n) _ -> enum end end @spec shortened_module_name(module | map, non_neg_integer) :: String.t() def shortened_module_name(mod, max_nesting \\ 2) def shortened_module_name(%{name: name}, max_nesting) when is_binary(name) do name |> String.split(".") |> last(max_nesting) |> Enum.join(".") end def shortened_module_name(mod, max_nesting) do mod |> Module.split() |> last(max_nesting) |> Enum.join(".") end @spec module_name(module | %{name: String.t()}) :: String.t() def module_name(%{name: name}) when is_binary(name), do: name def module_name(mod) when is_atom(mod) do mod |> Module.split() |> Enum.join(".") end @spec local_pid?(pid) :: boolean def local_pid?(pid) do case inspect(pid) do "#PID<0." <> _ -> true _ -> false end end def percentile_name(percentile) do p = percentile |> to_string |> String.replace(".", "_") :"percentile_#{p}" end def symbolize_keys(map) do map |> Map.new(fn {k, v} -> k = String.to_atom(k) v = case v do nested_map when is_map(nested_map) -> symbolize_keys(nested_map) _ -> v end {k, v} end) end end