defmodule Wac.Gen.Router do @moduledoc false alias Sourceror.Zipper alias Wac.Gen.GeneratorError @format_opts [ locals_without_parens: [ plug: :*, get: :*, scope: :*, delete: :*, live: :*, post: :*, forward: :*, live_dashboard: :*, live_session: :*, pipe_through: :* ] ] def update_router(assigns) do web_snake_case = Keyword.fetch!(assigns, :web_snake_case) router_path = Path.join(["lib", web_snake_case, "router.ex"]) modified_router_contents = router_path |> File.read!() |> Sourceror.parse_string!() |> Zipper.zip() |> Zipper.find(&find_aliases/1) |> insert_aliases(assigns) |> Zipper.find(&find_browser_pipeline/1) |> insert_plugs() |> Zipper.find(&find_first_scope/1) |> insert_routes(assigns) |> Zipper.root() |> Sourceror.to_string(@format_opts) File.write!(router_path, modified_router_contents) status = IO.ANSI.red() <> "* updating " <> IO.ANSI.reset() <> router_path IO.puts(status) end defp insert_aliases(%Zipper{} = zipper, assigns) do aliases = aliases(assigns) zipper |> Zipper.insert_right(aliases) end defp insert_aliases(nil, _assigns) do raise GeneratorError, "Unable to find aliases" end defp insert_plugs(%Zipper{} = zipper) do plug_fetch_current_user = plug_fetch_current_user() zipper |> Zipper.insert_right(plug_fetch_current_user) end defp insert_plugs(nil) do raise GeneratorError, "Unable to find the browser pipeline" end defp insert_routes(%Zipper{} = zipper, assigns) do session_routes = session_routes(assigns) guest_routes = guest_routes(assigns) authenticated_routes = authenticated_routes(assigns) zipper |> Zipper.insert_right(authenticated_routes) |> Zipper.insert_right(guest_routes) |> Zipper.insert_right(session_routes) end defp insert_routes(nil, _assigns) do raise GeneratorError, "Unable to find the first router scope" end defp find_aliases({:use, _meta, [{_, _, _}, {:__block__, _, [:router]} | _]}), do: true # defp find_aliases({:defmodule, _meta, [{_, _, _} | _]}), do: true defp find_aliases(_), do: false defp find_browser_pipeline({:plug, _, [{:__block__, _, [:put_secure_browser_headers]} | _]}), do: true defp find_browser_pipeline(_), do: false defp find_first_scope({:scope, _, [{:__block__, _, ["/"]} | _]}), do: true defp find_first_scope(_), do: false # AST Trees defp aliases(assigns) do web_pascal_case = assigns[:web_pascal_case] """ alias #{inspect(web_pascal_case)}.SessionHooks.AssignUser alias #{inspect(web_pascal_case)}.SessionHooks.RequireUser import #{inspect(web_pascal_case)}.Session, only: [fetch_current_user: 2] """ |> Sourceror.parse_string!() end defp plug_fetch_current_user do """ plug :fetch_current_user """ |> Sourceror.parse_string!() end defp session_routes(assigns) do web_pascal_case = assigns[:web_pascal_case] """ # HTTP controller routes scope "/", #{inspect(web_pascal_case)} do pipe_through :browser post "/session", Session, :create delete "/session", Session, :delete end """ |> Sourceror.parse_string!() end defp guest_routes(assigns) do web_pascal_case = assigns[:web_pascal_case] """ # Unprotected LiveViews live_session :guest, on_mount: [AssignUser] do scope "/", #{inspect(web_pascal_case)} do pipe_through :browser live "/sign-in", AuthenticationLive end end """ |> Sourceror.parse_string!() end defp authenticated_routes(assigns) do web_pascal_case = assigns[:web_pascal_case] """ # Protected LiveViews live_session :authenticated, on_mount: [AssignUser, RequireUser] do scope "/", #{inspect(web_pascal_case)} do pipe_through :browser # Example # live "/room/:room_id", RoomLive end end """ |> Sourceror.parse_string!() end end