defmodule Grazer.UI do use GenServer def start_link(arg) do GenServer.start_link(__MODULE__, arg) end def update(p, arg) do GenServer.cast(p, arg) end def info(p) when is_binary(p) do GenServer.call(p, :info) end @impl true def init(_init_arg) do port = Port.open({:spawn, path()}, [:binary]) ref = Port.monitor(port) {:ok, [port: port, ref: ref]} end @impl true def handle_call(:info, _from, state) do {:reply, {:ok, {Port.info(state[:port])}}, state} end @impl true def handle_cast(msg, state) do send(state[:port], {self(), {:command, msg <> "\n"}}) {:noreply, state} end @impl true def handle_info({:DOWN, ref, :port, port, :normal}, state) do IO.puts("down: #{inspect({ref, port})}") ^port = state[:port] ^ref = state[:ref] {:stop, :normal, state} end @impl true def handle_info([resp: {_status, _headers, body}], state) do # naively assuming content type is html {:ok, essense} = Grazer.HTML.distill(body) {:ok, json} = JSON.encode(essense) update(self(), json) {:noreply, state} end @impl true def handle_info({port, {:data, data}}, state) do IO.puts("data: #{inspect({port, data, state})}") ^port = state[:port] case String.split(data, " ") do ["navigate" | rest] -> Grazer.Net.request([ get: String.trim(Enum.join(rest, " ")), for: self(), ]) end {:noreply, state} end @impl true def handle_info(msg, state) do IO.puts("info: #{inspect({msg, state})}") {:noreply, state} end @impl true def terminate(_reason, state) do send(state[:port], {self(), :close}) :ok end defp path do "gui/target/debug/gui" "gui/target/release/gui" end end