defmodule Archeometer.Graphs.Mermaid do
@moduledoc """
Functions for working with mermaid-JS graphs
"""
@template """
graph TD;
<% mods_map =
@xrefs
|> Map.keys()
|> Enum.with_index()
|> Enum.map(fn {mod, idx} -> {mod, "id_" <> to_string(idx)} end)
|> Enum.into(%{})
%>
<%= for module <- Map.keys(@xrefs) do %>
<%= Map.get(mods_map, module) %>([<%= module %>]);
<% end %>
<%= for {mod, refs} <- @xrefs do %>
<%= for ref <- refs do %>
<%= Map.get(mods_map, mod) %>--><%= Map.get(mods_map, ref) %>;
<% end %>
<% end %>
"""
@doc """
Renders an graph represented by an adjacency list into .dot format.
Returns a string containing de rendered graph.
## Examples
iex(2)> graph = %{
...(2)> :a => [:b, :c],
...(2)> :b => [:c, :d],
...(2)> :d => [:a]
...(2)> }
%{a: [:b, :c], b: [:c, :d], d: [:a]}
iex(3)> str = Mermaid.render(graph)
iex(3)> assert is_binary(str)
"""
def render(xrefs) do
xrefs = add_independent_apps(xrefs)
@template
|> EEx.eval_string(assigns: [xrefs: xrefs])
|> String.replace(~r/(\n\s+\n)+|\n{2,}/, "\n")
end
defp add_independent_apps(xrefs) do
independent_apps = (Map.values(xrefs) |> List.flatten() |> Enum.uniq()) -- Map.keys(xrefs)
Enum.reduce(independent_apps, xrefs, fn app, new_adjacency_list ->
Map.put(new_adjacency_list, app, [])
end)
end
end