Fork of Pleroma with site-specific changes and feature branches https://git.pleroma.social/pleroma/pleroma
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

137 line
3.8KB

  1. # Pleroma: A lightweight social networking server
  2. # Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
  3. # SPDX-License-Identifier: AGPL-3.0-only
  4. defmodule Pleroma.Docs.Generator do
  5. @callback process(keyword()) :: {:ok, String.t()}
  6. @spec process(module(), keyword()) :: {:ok, String.t()}
  7. def process(implementation, descriptions) do
  8. implementation.process(descriptions)
  9. end
  10. @spec list_behaviour_implementations(behaviour :: module()) :: [module()]
  11. def list_behaviour_implementations(behaviour) do
  12. :code.all_loaded()
  13. |> Enum.filter(fn {module, _} ->
  14. # This shouldn't be needed as all modules are expected to have module_info/1,
  15. # but in test enviroments some transient modules `:elixir_compiler_XX`
  16. # are loaded for some reason (where XX is a random integer).
  17. if function_exported?(module, :module_info, 1) do
  18. module.module_info(:attributes)
  19. |> Keyword.get_values(:behaviour)
  20. |> List.flatten()
  21. |> Enum.member?(behaviour)
  22. end
  23. end)
  24. |> Enum.map(fn {module, _} -> module end)
  25. end
  26. @doc """
  27. Converts:
  28. - atoms to strings with leading `:`
  29. - module names to strings, without leading `Elixir.`
  30. - add humanized labels to `keys` if label is not defined, e.g. `:instance` -> `Instance`
  31. """
  32. @spec convert_to_strings([map()]) :: [map()]
  33. def convert_to_strings(descriptions) do
  34. Enum.map(descriptions, &format_entity(&1))
  35. end
  36. defp format_entity(entity) do
  37. entity
  38. |> format_key()
  39. |> Map.put(:group, atom_to_string(entity[:group]))
  40. |> format_children()
  41. end
  42. defp format_key(%{key: key} = entity) do
  43. entity
  44. |> Map.put(:key, atom_to_string(key))
  45. |> Map.put(:label, entity[:label] || humanize(key))
  46. end
  47. defp format_key(%{group: group} = entity) do
  48. Map.put(entity, :label, entity[:label] || humanize(group))
  49. end
  50. defp format_key(entity), do: entity
  51. defp format_children(%{children: children} = entity) do
  52. Map.put(entity, :children, Enum.map(children, &format_child(&1)))
  53. end
  54. defp format_children(entity), do: entity
  55. defp format_child(%{suggestions: suggestions} = entity) do
  56. entity
  57. |> Map.put(:suggestions, format_suggestions(suggestions))
  58. |> format_key()
  59. |> format_group()
  60. |> format_children()
  61. end
  62. defp format_child(entity) do
  63. entity
  64. |> format_key()
  65. |> format_group()
  66. |> format_children()
  67. end
  68. defp format_group(%{group: group} = entity) do
  69. Map.put(entity, :group, format_suggestion(group))
  70. end
  71. defp format_group(entity), do: entity
  72. defp atom_to_string(entity) when is_binary(entity), do: entity
  73. defp atom_to_string(entity) when is_atom(entity), do: inspect(entity)
  74. defp humanize(entity) do
  75. string = inspect(entity)
  76. if String.starts_with?(string, ":"),
  77. do: Phoenix.Naming.humanize(entity),
  78. else: string
  79. end
  80. defp format_suggestions({:list_behaviour_implementations, behaviour}) do
  81. behaviour
  82. |> list_behaviour_implementations()
  83. |> format_suggestions()
  84. end
  85. defp format_suggestions([]), do: []
  86. defp format_suggestions([suggestion | tail]) do
  87. [format_suggestion(suggestion) | format_suggestions(tail)]
  88. end
  89. defp format_suggestion(entity) when is_atom(entity) do
  90. atom_to_string(entity)
  91. end
  92. defp format_suggestion([head | tail] = entity) when is_list(entity) do
  93. [format_suggestion(head) | format_suggestions(tail)]
  94. end
  95. defp format_suggestion(entity) when is_tuple(entity) do
  96. format_suggestions(Tuple.to_list(entity)) |> List.to_tuple()
  97. end
  98. defp format_suggestion(entity), do: entity
  99. end
  100. defimpl Jason.Encoder, for: Tuple do
  101. def encode(tuple, opts), do: Jason.Encode.list(Tuple.to_list(tuple), opts)
  102. end
  103. defimpl Jason.Encoder, for: [Regex, Function] do
  104. def encode(term, opts), do: Jason.Encode.string(inspect(term), opts)
  105. end
  106. defimpl String.Chars, for: Regex do
  107. def to_string(term), do: inspect(term)
  108. end