|
| 1 | +defmodule Reactor.Mermaid.Node do |
| 2 | + @moduledoc """ |
| 3 | + A node in the mermaid graph |
| 4 | + """ |
| 5 | + defstruct children: [], id: nil, post: [], pre: [] |
| 6 | + |
| 7 | + @type node_data :: node_list | binary | byte | t |
| 8 | + @type node_list :: [node_data] | node_data |
| 9 | + |
| 10 | + @type t :: %__MODULE__{ |
| 11 | + children: node_list, |
| 12 | + id: String.t(), |
| 13 | + post: node_list, |
| 14 | + pre: node_list |
| 15 | + } |
| 16 | + |
| 17 | + @indent " " |
| 18 | + |
| 19 | + @doc """ |
| 20 | + Render the node at the provided indent level. |
| 21 | + """ |
| 22 | + @spec render(t, non_neg_integer()) :: iodata |
| 23 | + def render(node, indent) do |
| 24 | + indent = |
| 25 | + [@indent] |
| 26 | + |> Stream.cycle() |
| 27 | + |> Enum.take(indent) |
| 28 | + |
| 29 | + do_render([node], [], indent) |
| 30 | + end |
| 31 | + |
| 32 | + defguardp is_byte(byte) when is_integer(byte) and byte >= 0 and byte <= 255 |
| 33 | + |
| 34 | + defp do_render([], result, _indent), do: result |
| 35 | + defp do_render([[] | tail], result, indent), do: do_render(tail, result, indent) |
| 36 | + |
| 37 | + defp do_render([head | tail], result, indent) when is_list(head) do |
| 38 | + head = do_render(head, [], indent) |
| 39 | + do_render(tail, [result, head], indent) |
| 40 | + end |
| 41 | + |
| 42 | + defp do_render([head | tail], result, indent) when is_struct(head, __MODULE__) do |
| 43 | + pre = do_render(head.pre, [], indent) |
| 44 | + children = do_render(head.children, [], [indent, @indent]) |
| 45 | + post = do_render(head.post, [], indent) |
| 46 | + do_render(tail, [result, pre, children, post], indent) |
| 47 | + end |
| 48 | + |
| 49 | + defp do_render([head | tail], result, indent) when is_binary(head), |
| 50 | + do: do_render(tail, [result, indent(head, indent)], indent) |
| 51 | + |
| 52 | + defp do_render([head | tail], result, indent) |
| 53 | + when is_integer(head) and head >= 0 and head <= 255, |
| 54 | + do: do_render(tail, [result, indent(head, indent)], indent) |
| 55 | + |
| 56 | + defp do_render(bin, result, indent) when is_binary(bin), |
| 57 | + do: do_render([], [result, indent(bin, indent)], indent) |
| 58 | + |
| 59 | + defp do_render(int, result, indent) when is_byte(int), |
| 60 | + do: do_render([], [result, indent(int, indent)], indent) |
| 61 | + |
| 62 | + defp indent("", _indent), do: "" |
| 63 | + |
| 64 | + defp indent(input, []), do: input |
| 65 | + |
| 66 | + defp indent(bin, indent) when is_binary(bin) do |
| 67 | + bin |
| 68 | + |> String.split("\n") |
| 69 | + |> Enum.intersperse(["\n", indent]) |
| 70 | + end |
| 71 | + |
| 72 | + defp indent(?\n, indent) do |
| 73 | + [?\n, indent] |
| 74 | + end |
| 75 | + |
| 76 | + defp indent(int, _indent) when is_byte(int), do: IO.chardata_to_string([int]) |
| 77 | +end |
0 commit comments