defmodule Vial do
@moduledoc """
`Vial` is a library for creating Vials, which are self-contained units of functionality that operate on data and create events.
"""
alias Vial.Cauldron
@callback init(any()) :: any()
@callback call(Vial.Glass.t(), any()) :: Vial.Glass.t()
def run(cauldron, vials, reagents, opts \\ [])
def run(%Cauldron{halted: true} = cauldron, _vials, reagents, _opts) do
do_events(cauldron, reagents)
end
def run(%Cauldron{} = cauldron, vials, reagents, _opts) do
do_run(cauldron, vials)
|> do_events(reagents)
end
defp do_run(%Cauldron{} = cauldron, [{vial_mod, vial_opts} | vials]) do
case vial_mod.call(cauldron, vial_mod.init(vial_opts)) do
%Cauldron{halted: true} = cauldron ->
cauldron
%Cauldron{} = cauldron ->
do_run(cauldron, vials)
other ->
raise "expected #{inspect(vial_mod)} to return Vial.Cauldron, got: #{inspect(other)}"
end
end
defp do_run(%Cauldron{} = cauldron, [fun | vials]) when is_function(fun, 1) do
case fun.(cauldron) do
%Cauldron{halted: true} = cauldron ->
cauldron
%Cauldron{} = cauldron ->
do_run(cauldron, vials)
other ->
raise "expected #{inspect(fun)} to return Vial.Cauldron, got: #{inspect(other)}"
end
end
defp do_run(%Cauldron{} = cauldron, []) do
cauldron
end
defp do_events(%Cauldron{} = cauldron, [reagent_fun | reagents]) when is_function(reagent_fun, 1) do
events = Enum.reverse(cauldron.events)
Enum.each(events, reagent_fun)
do_events(cauldron, reagents)
end
defp do_events(%Cauldron{} = cauldron, [{reagent_mod, reagent_opts} | reagents]) do
events = Enum.reverse(cauldron.events)
processed_reagent_opts = reagent_mod.init(reagent_opts)
Enum.each(events, &reagent_mod.handle(&1, processed_reagent_opts))
do_events(cauldron, reagents)
end
defp do_events(%Cauldron{} = cauldron, []) do
cauldron
end
end