summaryrefslogtreecommitdiff
path: root/lib/pleroma/docs/generator.ex
blob: e0fc8cd02144de31766dbb22a204d1e53e7aa5d3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
defmodule Pleroma.Docs.Generator do
  @callback process(keyword()) :: {:ok, String.t()}

  @spec process(module(), keyword()) :: {:ok, String.t()}
  def process(implementation, descriptions) do
    implementation.process(descriptions)
  end

  @spec list_modules_in_dir(String.t(), String.t()) :: [module()]
  def list_modules_in_dir(dir, start) do
    with {:ok, files} <- File.ls(dir) do
      files
      |> Enum.filter(&String.ends_with?(&1, ".ex"))
      |> Enum.map(fn filename ->
        module = filename |> String.trim_trailing(".ex") |> Macro.camelize()
        String.to_atom(start <> module)
      end)
    end
  end

  @doc """
  Converts:
  - atoms to strings with leading `:`
  - module names to strings, without leading `Elixir.`
  - add humanized labels to `keys` if label is not defined, e.g. `:instance` -> `Instance`
  """
  @spec convert_to_strings([map()]) :: [map()]
  def convert_to_strings(descriptions) do
    Enum.map(descriptions, &format_entity(&1))
  end

  defp format_entity(entity) do
    entity
    |> format_key()
    |> Map.put(:group, atom_to_string(entity[:group]))
    |> format_children()
  end

  defp format_key(%{key: key} = entity) do
    entity
    |> Map.put(:key, atom_to_string(key))
    |> Map.put(:label, entity[:label] || humanize(key))
  end

  defp format_key(%{group: group} = entity) do
    Map.put(entity, :label, entity[:label] || humanize(group))
  end

  defp format_key(entity), do: entity

  defp format_children(%{children: children} = entity) do
    Map.put(entity, :children, Enum.map(children, &format_child(&1)))
  end

  defp format_children(entity), do: entity

  defp format_child(%{suggestions: suggestions} = entity) do
    entity
    |> Map.put(:suggestions, format_suggestions(suggestions))
    |> format_key()
    |> format_group()
    |> format_children()
  end

  defp format_child(entity) do
    entity
    |> format_key()
    |> format_group()
    |> format_children()
  end

  defp format_group(%{group: group} = entity) do
    Map.put(entity, :group, format_suggestion(group))
  end

  defp format_group(entity), do: entity

  defp atom_to_string(entity) when is_binary(entity), do: entity

  defp atom_to_string(entity) when is_atom(entity), do: inspect(entity)

  defp humanize(entity) do
    string = inspect(entity)

    if String.starts_with?(string, ":"),
      do: Phoenix.Naming.humanize(entity),
      else: string
  end

  defp format_suggestions([]), do: []

  defp format_suggestions([suggestion | tail]) do
    [format_suggestion(suggestion) | format_suggestions(tail)]
  end

  defp format_suggestion(entity) when is_atom(entity) do
    atom_to_string(entity)
  end

  defp format_suggestion([head | tail] = entity) when is_list(entity) do
    [format_suggestion(head) | format_suggestions(tail)]
  end

  defp format_suggestion(entity) when is_tuple(entity) do
    format_suggestions(Tuple.to_list(entity)) |> List.to_tuple()
  end

  defp format_suggestion(entity), do: entity
end

defimpl Jason.Encoder, for: Tuple do
  def encode(tuple, opts), do: Jason.Encode.list(Tuple.to_list(tuple), opts)
end

defimpl Jason.Encoder, for: [Regex, Function] do
  def encode(term, opts), do: Jason.Encode.string(inspect(term), opts)
end

defimpl String.Chars, for: Regex do
  def to_string(term), do: inspect(term)
end