lib/generator.ex

defmodule PhoenixAutoDoc.Generator do
  def get_info(web_module) do
    items_list =
      apply(:"#{web_module}.Router", :__routes__, [])
      |> Enum.filter(fn i -> not Regex.match?(~r/PhoenixAutoDoc/, "#{i.plug}") end)
      |> Enum.group_by(& &1.plug)
      |> Map.to_list()
      |> Enum.map(fn {module, routes_list} ->
        {_, _, _, _, doc, _, functions_list} = Code.fetch_docs(module)

        routes_list =
          Enum.map(routes_list, fn route ->
            route_map = %{
              url: route.path,
              method: Atom.to_string(route.verb) |> String.upcase(),
              function: route.plug_opts
            }

            Enum.find(functions_list, fn {{_, name, _}, _, _, _, _} ->
              name == route.plug_opts
            end)
            |> case do
              {{_, _, _}, _, _, doc, _} ->
                Map.merge(route_map, %{
                  title: documentation_handler(doc) |> title(),
                  documentation: documentation_handler(doc)
                })

              nil ->
                Map.merge(route_map, %{title: nil, documentation: nil})
            end
          end)

        %{
          module: module,
          title: documentation_handler(doc) |> title(),
          documentation: documentation_handler(doc),
          routes: routes_list
        }
      end)
      |> Enum.group_by(& &1.module)
      |> Map.to_list()
      |> Enum.map(fn {key, [data]} -> {key, data} end)

    [{web_module, get_documentation(web_module)}]
    |> Enum.concat(items_list)
    |> Map.new()
  end

  defp get_documentation(module_name) do
    {_, _, _, _, doc, _, _} = Code.fetch_docs(module_name)

    %{
      module: module_name,
      title: documentation_handler(doc) |> title(),
      documentation: documentation_handler(doc),
      routes: []
    }
  end

  defp title(str) when is_bitstring(str) do
    case Regex.run(~r/^# (.+?)\n/, str) do
      [_, title] -> title
      _any -> nil
    end
  end

  defp title(_any), do: nil

  defp documentation_handler(%{"en" => doc}), do: doc
  defp documentation_handler(_any), do: nil
end