lib/hologram/runtime/router_builder.ex

# DEFER: test

defmodule Hologram.Runtime.RouterBuilder do
  use GenServer

  alias Hologram.Compiler.Reflection
  alias Hologram.Router

  def start_link(_) do
    GenServer.start_link(__MODULE__, [], name: __MODULE__)
  end

  def init(_) do
    create_matcher_module()
    {:ok, nil}
  end

  defp add_route_segment_info(route_segment) do
    case route_segment do
      ":" <> name -> {:param, name}
      name -> {:literal, name}
    end
  end

  defp build_function_params(route_segments) do
    tuple_elems =
      Enum.map(route_segments, fn
        {:param, name} -> name
        {:literal, name} -> ~s("#{name}")
      end)
      |> Enum.join(", ")

    "{" <> tuple_elems <> "}"
  end

  defp build_match_function_def(page) do
    route_segments = get_route_segments(page)
    function_params = build_function_params(route_segments)
    page_params = build_page_params(route_segments)

    """
    def match(#{function_params}) do
      {#{page}, #{page_params}}
    end
    """
  end

  defp build_page_params(route_segments) do
    map_elems =
      route_segments
      |> Enum.reject(fn {type, _} -> type == :literal end)
      |> Enum.map(fn {_, name} -> "#{name}: #{name}" end)
      |> Enum.join(", ")

    "%{" <> map_elems <> "}"
  end

  defp create_matcher_module do
    function_defs =
      Reflection.list_release_pages()
      |> Enum.reduce("", fn page, acc ->
        "#{acc}#{build_match_function_def(page)}\n"
      end)

    module_body = """
    #{function_defs}
    def match(_), do: nil
    """

    ast = Reflection.ast(module_body)

    Module.create(Hologram.Runtime.RouterMatcher, ast, Macro.Env.location(__ENV__))
  end

  defp get_route_segments(page) do
    page.route()
    |> Router.get_path_segments()
    |> Enum.map(&add_route_segment_info/1)
  end
end