lib/file_system/worker.ex

require Logger

defmodule FileSystem.Worker do
  @moduledoc """
  FileSystem Worker Process with the backend GenServer, receive events from Port Process
  and forward it to subscribers.
  """

  use GenServer

  @doc false
  def start_link(args) do
    {opts, args} = Keyword.split(args, [:name])
    GenServer.start_link(__MODULE__, args, opts)
  end

  @doc false
  def init(args) do
    {backend, rest} = Keyword.pop(args, :backend)

    with {:ok, backend} <- FileSystem.Backend.backend(backend),
         {:ok, backend_pid} <- backend.start_link([{:worker_pid, self()} | rest]) do
      {:ok, %{backend_pid: backend_pid, subscribers: %{}}}
    else
      reason ->
        Logger.warning("Not able to start file_system worker, reason: #{inspect(reason)}")
        :ignore
    end
  end

  @doc false
  def handle_call(:subscribe, {pid, _}, state) do
    ref = Process.monitor(pid)
    state = put_in(state, [:subscribers, ref], pid)
    {:reply, :ok, state}
  end

  @doc false
  def handle_info(
        {:backend_file_event, backend_pid, file_event},
        %{backend_pid: backend_pid} = state
      ) do
    state.subscribers
    |> Enum.each(fn {_ref, subscriber_pid} ->
      send(subscriber_pid, {:file_event, self(), file_event})
    end)

    {:noreply, state}
  end

  def handle_info({:DOWN, ref, _, _pid, _reason}, state) do
    subscribers = Map.drop(state.subscribers, [ref])
    {:noreply, %{state | subscribers: subscribers}}
  end

  def handle_info(_, state) do
    {:noreply, state}
  end
end