]> Entropealabs - wampex_client.git/commitdiff
adds macros for easier handling of registrations and subscriptions
authorChristopher Coté <chris@entropealabs.com>
Tue, 2 Nov 2021 01:15:18 +0000 (21:15 -0400)
committerChristopher Coté <chris@entropealabs.com>
Tue, 2 Nov 2021 01:15:18 +0000 (21:15 -0400)
lib/client/handler.ex [new file with mode: 0644]

diff --git a/lib/client/handler.ex b/lib/client/handler.ex
new file mode 100644 (file)
index 0000000..e90664a
--- /dev/null
@@ -0,0 +1,128 @@
+defmodule Wampex.Client.Handler do
+  @moduledoc """
+  Macros and behaviours for client implemenations
+  """
+  @callback do_init(any()) :: %{client_name: atom()}
+
+  defmacro __using__(_opts) do
+    quote do
+      use GenServer
+      alias Wampex.Client
+      alias Wampex.Roles.Callee.Yield
+      alias Wampex.Roles.Peer.Error
+      import unquote(__MODULE__)
+      @before_compile unquote(__MODULE__)
+      @procedures []
+      @topics []
+
+      def start_link(opts) do
+        {name, opts} = Keyword.pop(opts, :name)
+        GenServer.start_link(__MODULE__, opts, name: name)
+      end
+
+      @impl true
+      def init(opts) do
+        {cn, opts} = Keyword.pop(opts, :client_name)
+        state = do_init(opts)
+        Client.add(cn, self())
+        {:ok, %{state | client_name: cn}}
+      end
+
+      @impl true
+      def handle_continue({:registered, _}, state), do: {:noreply, state}
+
+      def do_init(_opts), do: %{client_name: nil}
+
+      def handle_invocation_block({:ok, al, kw, state}, id) do
+        Client.yield(state.client_name, %Yield{request_id: id, arg_list: al, arg_kw: kw})
+        state
+      end
+
+      def handle_invocation_block({:error, error, al, kw, state}, id) do
+        Client.error(state.client_name, %Error{
+          request_id: id,
+          error: error,
+          arg_list: al,
+          arg_kw: kw
+        })
+
+        state
+      end
+
+      defoverridable do_init: 1, handle_continue: 2
+    end
+  end
+
+  defmacro __before_compile__(env) do
+    procedures = Module.get_attribute(env.module, :procedures)
+    topics = Module.get_attribute(env.module, :topics)
+
+    quote do
+      alias Wampex.Client
+      alias Wampex.Roles.Callee.Register
+      alias Wampex.Roles.Subscriber.Subscribe
+      @impl true
+      def handle_info({:connected, _}, %{client_name: cn} = state) do
+        regs =
+          Enum.map(unquote(procedures), fn p ->
+            Client.register(cn, %Register{procedure: p})
+          end)
+
+        subs =
+          Enum.map(unquote(topics), fn
+            {t, match} ->
+              Client.subscribe(cn, %Subscribe{topic: t, options: %{"match" => match}})
+
+            t ->
+              Client.subscribe(cn, %Subscribe{topic: t})
+          end)
+
+        {:noreply, state, {:continue, {:registered, %{registrations: regs, subscriptions: subs}}}}
+      end
+    end
+  end
+
+  defmacro invocation(procedure, list, kws, state, do: block) do
+    quote location: :keep do
+      alias Wampex.Roles.Dealer.Invocation
+
+      @procedures [unquote(procedure) | @procedures]
+
+      @impl true
+      def handle_info(
+            %Invocation{
+              request_id: id,
+              details: %{"procedure" => unquote(procedure)},
+              arg_list: unquote(list),
+              arg_kw: unquote(kws)
+            },
+            unquote(state) = state
+          ) do
+        state = handle_invocation_block(unquote(block), id)
+        {:noreply, state}
+      end
+    end
+  end
+
+  defmacro event(topic, list, kws, state, do: block) do
+    quote location: :keep do
+      alias Wampex.Client
+      alias Wampex.Roles.Broker.Event
+
+      @topics [unquote(topic) | @topics]
+
+      @impl true
+      def handle_info(
+            %Event{
+              details: %{"topic" => unquote(topic)},
+              arg_list: unquote(list),
+              arg_kw: unquote(kws)
+            },
+            unquote(state) = state
+          ) do
+        state = unquote(block)
+        {:noreply, state}
+      end
+    end
+  end
+end