defmodule TaskAwaiter do use GenServer defstruct [:state, :condition_verifier, :result_acceptor, :awaiter_notifier] # API def start_link(initial_state, initializer, condition_verifier, result_acceptor, awaiter_notifier, gen_server_options \\ []) do GenServer.start_link(__MODULE__, [initial_state, initializer, condition_verifier, result_acceptor, awaiter_notifier], gen_server_options) end def task_completed(server, result) do server |> GenServer.cast({:accept_result, result}) end # Callbacks def init([initial_state, initializer, condition_verifier, result_acceptor, awaiter_notifier]) do if initializer do initializer.(initial_state) else initial_state end {:ok, %TaskAwaiter{state: initial_state, condition_verifier: condition_verifier, result_acceptor: result_acceptor, awaiter_notifier: awaiter_notifier}} end def handle_cast({:accept_result, result}, %TaskAwaiter{state: awaiter_state, result_acceptor: result_acceptor, condition_verifier: condition_verifier, awaiter_notifier: awaiter_notifier} = state) do new_awaiter_state = awaiter_state |> result_acceptor.(result) |> coalesce(condition_verifier, awaiter_notifier) {:noreply, %{state|state: new_awaiter_state}} end def handle_info({ref, _result} = msg, state) when ref |> is_reference() do self() |> task_completed(msg) {:noreply, state} end def handle_info(_, state) do {:noreply, state} end # Helper functions def coalesce(value, condition, transformation) do if condition.(value) do transformation.(value) else value end end end