Current section
Files
Jump to
Current section
Files
lib/tw_merge/class_tree.ex
defmodule TwMerge.ClassTree do
@moduledoc false
alias TwMerge.Cache
alias TwMerge.Config
def get do
case Cache.retrieve(:class_tree) do
nil ->
class_tree = generate()
Cache.insert(:class_tree, class_tree)
class_tree
class_tree ->
class_tree
end
end
def generate do
config = Config.config()
theme = Map.get(config, :theme, %{})
prefix = Map.get(config, :prefix)
groups = Map.get(config, :groups, [])
groups
|> Enum.reduce(%{}, fn {group, next}, acc ->
DeepMerge.deep_merge(acc, handle_next(next, theme, group))
end)
|> add_prefix(prefix)
end
defp handle_next(next, theme, group) do
Enum.reduce(next, %{"next" => %{}, "validators" => %{}}, fn part, acc ->
case part do
{sub_group, sub_next} ->
add_group(sub_group, sub_next, theme, group, acc)
part ->
add_part(part, theme, group, acc)
end
end)
end
defp add_part(part, _theme, group, acc) when is_binary(part) do
next =
part
|> String.split("-")
|> case do
[] -> %{}
[single] -> %{single => %{"group" => group, "next" => %{}, "validators" => %{}}}
multiple -> add_part_recursive(multiple, group)
end
Map.update(acc, "next", %{}, &DeepMerge.deep_merge(&1, next))
end
defp add_part(part, theme, group, acc) when is_map(part) do
DeepMerge.deep_merge(acc, handle_next(part, theme, group))
end
defp add_part(part, theme, group, acc) when is_function(part) do
if from_theme?(part) do
flattened_theme = flatten_theme(part, theme, group)
acc
|> Map.update("next", %{}, &DeepMerge.deep_merge(&1, flattened_theme["next"]))
|> Map.update("validators", %{}, &DeepMerge.deep_merge(&1, flattened_theme["validators"]))
else
{:name, key} = Function.info(part, :name)
Map.update(acc, "validators", %{}, fn validators ->
Map.put(validators, to_string(key), %{"function" => part, "group" => group})
end)
end
end
defp add_part_recursive([single], group) do
%{single => %{"group" => group, "next" => %{}, "validators" => %{}}}
end
defp add_part_recursive([current | rest], group) do
%{current => %{"next" => add_part_recursive(rest, group)}}
end
defp add_group(sub_group, sub_next, theme, group, acc) do
sub_group
|> String.split("-")
|> add_nested_group(sub_next, theme, group, acc)
end
defp add_nested_group([current | rest], sub_next, theme, group, acc) do
Map.update(acc, "next", %{}, &Map.put(&1, current, add_nested_group(rest, List.flatten(sub_next), theme, group, acc)))
end
defp add_nested_group([], sub_next, theme, group, _acc) do
handle_next(sub_next, theme, group)
end
defp from_theme?(function), do: Function.info(function, :module) == {:module, Config}
defp flatten_theme(function, theme, group, acc \\ %{"next" => %{}, "validators" => %{}}) do
Enum.reduce(function.(theme), acc, fn
item, acc when is_binary(item) ->
Map.update(acc, "next", %{}, &Map.put(&1, item, %{"next" => %{}, "validators" => %{}, "group" => group}))
item, acc when is_function(item) ->
if from_theme?(item) do
flatten_theme(item, theme, group, acc)
else
Map.update(acc, "validators", %{}, fn validators ->
{:name, key} = Function.info(item, :name)
Map.put(validators, to_string(key), %{"function" => item, "group" => group})
end)
end
end)
end
defp add_prefix(groups, nil), do: groups
defp add_prefix(groups, prefix) do
%{"next" => %{String.trim_trailing(prefix, "-") => groups}, "validators" => %{}}
end
end