diff --git a/lib/reactor.ex b/lib/reactor.ex index bf98a98..8dc00d9 100644 --- a/lib/reactor.ex +++ b/lib/reactor.ex @@ -44,7 +44,6 @@ defmodule Reactor do defstruct context: %{}, description: nil, id: nil, - input_descriptions: %{}, inputs: [], intermediate_results: %{}, middleware: [], @@ -144,8 +143,7 @@ defmodule Reactor do context: context, description: nil | String.t(), id: any, - input_descriptions: %{atom => String.t()}, - inputs: [atom], + inputs: [Reactor.Input.t()], intermediate_results: %{any => any}, middleware: [Reactor.Middleware.t()], plan: nil | Graph.t(), diff --git a/lib/reactor/builder/compose.ex b/lib/reactor/builder/compose.ex index ee3dfbd..e72c9d4 100644 --- a/lib/reactor/builder/compose.ex +++ b/lib/reactor/builder/compose.ex @@ -138,7 +138,7 @@ defmodule Reactor.Builder.Compose do end defp reactor_inputs(reactor) when is_struct(reactor), - do: {:ok, MapSet.new(reactor.inputs)} + do: {:ok, MapSet.new(reactor.inputs, & &1.name)} defp reactor_inputs(reactor) when is_atom(reactor) do with {:ok, reactor} <- Reactor.Info.to_struct(reactor) do diff --git a/lib/reactor/builder/input.ex b/lib/reactor/builder/input.ex index 9090d39..67d8357 100644 --- a/lib/reactor/builder/input.ex +++ b/lib/reactor/builder/input.ex @@ -43,38 +43,34 @@ defmodule Reactor.Builder.Input do @doc """ Add a named input to the reactor. """ - @spec add_input(Reactor.t(), any, options) :: {:ok, Reactor.t()} | {:error, any} - def add_input(reactor, name, options) do - case validate_options(options) do - {:ok, options} when is_nil(options.transform) -> - reactor = - reactor - |> do_add_input(name) - |> maybe_add_description(name, options.description) + @spec add_input(Reactor.t(), any, options | transform) :: {:ok, Reactor.t()} | {:error, any} + def add_input(reactor, name, nil), do: add_input(reactor, name, []) - {:ok, reactor} - - {:ok, options} -> - reactor = - reactor - |> do_add_input(name) - |> add_input_transform(name, options.transform) - |> maybe_add_description(name, options.description) - - {:ok, reactor} + def add_input(reactor, name, options) when is_list(options) do + with {:ok, options} <- Spark.Options.validate(options, @options) do + reactor = + reactor + |> do_add_input(name, options) + |> maybe_add_input_transform(name, options[:transform]) - {:error, reason} -> - {:error, reason} + {:ok, reactor} end end - defp do_add_input(reactor, name), do: %{reactor | inputs: [name | reactor.inputs]} - defp maybe_add_description(reactor, _name, nil), do: reactor + def add_input(reactor, name, transform), do: add_input(reactor, name, transform: transform) + + defp do_add_input(reactor, name, options) do + input = %Reactor.Input{ + name: name, + description: options[:description] + } + + %{reactor | inputs: [input | reactor.inputs]} + end - defp maybe_add_description(reactor, name, description), - do: %{reactor | input_descriptions: Map.put(reactor.input_descriptions, name, description)} + defp maybe_add_input_transform(reactor, _name, nil), do: reactor - defp add_input_transform(reactor, name, {module, options} = transform) + defp maybe_add_input_transform(reactor, name, {module, options} = transform) when is_atom(module) and is_list(options) do transform_step = %Step{ arguments: [Argument.from_input(:value, name)], @@ -89,14 +85,6 @@ defmodule Reactor.Builder.Input do %{reactor | steps: [transform_step | reactor.steps]} end - defp add_input_transform(reactor, name, transform), - do: add_input_transform(reactor, name, {Step.Transform, fun: transform}) - - defp validate_options(options) when is_list(options) do - with {:ok, options} <- Spark.Options.validate(options, @options) do - {:ok, Map.new(options)} - end - end - - defp validate_options(transform), do: validate_options(transform: transform) + defp maybe_add_input_transform(reactor, name, transform), + do: maybe_add_input_transform(reactor, name, {Step.Transform, fun: transform}) end diff --git a/lib/reactor/executor/init.ex b/lib/reactor/executor/init.ex index b10b0b9..23acbdd 100644 --- a/lib/reactor/executor/init.ex +++ b/lib/reactor/executor/init.ex @@ -51,11 +51,11 @@ defmodule Reactor.Executor.Init do end defp validate_inputs(reactor, inputs) do - valid_input_names = MapSet.new(reactor.inputs) + valid_input_names = MapSet.new(reactor.inputs, & &1.name) provided_input_names = inputs |> Map.keys() |> MapSet.new() if MapSet.subset?(valid_input_names, provided_input_names) do - {:ok, Map.take(inputs, reactor.inputs)} + {:ok, Map.take(inputs, Enum.to_list(valid_input_names))} else missing_inputs = valid_input_names diff --git a/lib/reactor/input.ex b/lib/reactor/input.ex new file mode 100644 index 0000000..0b8cb43 --- /dev/null +++ b/lib/reactor/input.ex @@ -0,0 +1,16 @@ +# SPDX-FileCopyrightText: 2023 James Harton, Zach Daniel, Alembic Pty and contributors +# SPDX-FileCopyrightText: 2023 reactor contributors +# +# SPDX-License-Identifier: MIT + +defmodule Reactor.Input do + @moduledoc """ + Reactor's internal representation for inputs. + """ + defstruct [:name, :description] + + @type t :: %__MODULE__{ + name: atom, + description: nil | String.t() + } +end diff --git a/lib/reactor/mermaid/reactor.ex b/lib/reactor/mermaid/reactor.ex index c886bb5..89c9781 100644 --- a/lib/reactor/mermaid/reactor.ex +++ b/lib/reactor/mermaid/reactor.ex @@ -57,8 +57,8 @@ defmodule Reactor.Mermaid.Reactor do |> map_while_ok(&generate_input(&1, reactor, options)) end - defp generate_input(input_name, reactor, options) do - id = mermaid_id({reactor.id, input_name}, :input) + defp generate_input(input, reactor, options) do + id = mermaid_id({reactor.id, input.name}, :input) content = if options[:describe?] do @@ -66,15 +66,15 @@ defmodule Reactor.Mermaid.Reactor do id, ">\"`", "**Input ", - to_string(input_name), + to_string(input.name), "**", "\n", - md_escape(Map.get(reactor.input_descriptions, input_name)), + md_escape(input.description), "`\"]", "\n" ] else - [id, ">\"Input ", to_string(input_name), "\"]\n"] + [id, ">\"Input ", to_string(input.name), "\"]\n"] end {:ok, %Node{id: id, pre: content}} diff --git a/lib/reactor/step/around/mermaid.ex b/lib/reactor/step/around/mermaid.ex index e10170a..a8b7402 100644 --- a/lib/reactor/step/around/mermaid.ex +++ b/lib/reactor/step/around/mermaid.ex @@ -20,7 +20,7 @@ defmodule Reactor.Step.Around.Mermaid do links = reactor.inputs |> Enum.map(fn input -> - [node.id, "-->", mermaid_id({reactor.id, input}, :input), "\n"] + [node.id, "-->", mermaid_id({reactor.id, input.name}, :input), "\n"] end) |> Enum.concat([inner_return_id, "-->", node.id, "\n"]) diff --git a/lib/reactor/step/compose/mermaid.ex b/lib/reactor/step/compose/mermaid.ex index ac0d2be..9050443 100644 --- a/lib/reactor/step/compose/mermaid.ex +++ b/lib/reactor/step/compose/mermaid.ex @@ -28,7 +28,7 @@ defmodule Reactor.Step.Compose.Mermaid do links = reactor.inputs |> Enum.map(fn input -> - [node.id, "-->", mermaid_id({reactor.id, input}, :input), "\n"] + [node.id, "-->", mermaid_id({reactor.id, input.name}, :input), "\n"] end) |> Enum.concat([inner_return_id, "-->", node.id, "\n"]) diff --git a/lib/reactor/step/group/mermaid.ex b/lib/reactor/step/group/mermaid.ex index 80e13dc..42da552 100644 --- a/lib/reactor/step/group/mermaid.ex +++ b/lib/reactor/step/group/mermaid.ex @@ -20,7 +20,7 @@ defmodule Reactor.Step.Group.Mermaid do links = reactor.inputs |> Enum.map(fn input -> - [node.id, "-->", mermaid_id({reactor.id, input}, :input), "\n"] + [node.id, "-->", mermaid_id({reactor.id, input.name}, :input), "\n"] end) |> Enum.concat([inner_return_id, "-->", node.id, "\n"]) diff --git a/lib/reactor/step/map/mermaid.ex b/lib/reactor/step/map/mermaid.ex index b8181c0..ba4f60c 100644 --- a/lib/reactor/step/map/mermaid.ex +++ b/lib/reactor/step/map/mermaid.ex @@ -25,7 +25,7 @@ defmodule Reactor.Step.Map.Mermaid do links = reactor.inputs |> Enum.map(fn input -> - [node.id, "-->", mermaid_id({reactor.id, input}, :input), "\n"] + [node.id, "-->", mermaid_id({reactor.id, input.name}, :input), "\n"] end) |> Enum.concat([inner_return_id, "-->", node.id, "\n"]) diff --git a/lib/reactor/step/recurse/mermaid.ex b/lib/reactor/step/recurse/mermaid.ex index d6b0ea0..cc6d2ce 100644 --- a/lib/reactor/step/recurse/mermaid.ex +++ b/lib/reactor/step/recurse/mermaid.ex @@ -29,7 +29,7 @@ defmodule Reactor.Step.Recurse.Mermaid do links = reactor.inputs |> Enum.map(fn input -> - [node.id, "-->", mermaid_id({reactor.id, input}, :input), "\n"] + [node.id, "-->", mermaid_id({reactor.id, input.name}, :input), "\n"] end) |> Enum.concat([inner_return_id, "-->", node.id, "\n"]) diff --git a/test/reactor/builder/input_test.exs b/test/reactor/builder/input_test.exs index a638e81..7665e1b 100644 --- a/test/reactor/builder/input_test.exs +++ b/test/reactor/builder/input_test.exs @@ -14,7 +14,7 @@ defmodule Reactor.Builder.InputTest do Builder.new() |> Input.add_input(:marty, nil) - assert :marty in reactor.inputs + assert %Reactor.Input{name: :marty} in reactor.inputs assert [] = reactor.steps end @@ -23,7 +23,7 @@ defmodule Reactor.Builder.InputTest do Builder.new() |> Input.add_input(:marty, &Function.identity/1) - assert :marty in reactor.inputs + assert %Reactor.Input{name: :marty} in reactor.inputs assert [step] = reactor.steps assert step.name == {:__reactor__, :transform, :input, :marty} @@ -35,7 +35,7 @@ defmodule Reactor.Builder.InputTest do Builder.new() |> Input.add_input(:marty, {Step.Transform, fun: &Function.identity/1}) - assert :marty in reactor.inputs + assert %Reactor.Input{name: :marty} in reactor.inputs assert [step] = reactor.steps assert step.name == {:__reactor__, :transform, :input, :marty} diff --git a/test/reactor/builder_test.exs b/test/reactor/builder_test.exs index 3d2e9f3..245755a 100644 --- a/test/reactor/builder_test.exs +++ b/test/reactor/builder_test.exs @@ -23,12 +23,12 @@ defmodule Reactor.BuilderTest do test "when the input doesn't have a transformer, it adds the input" do {:ok, reactor} = add_input(new(), :marty) - assert :marty in reactor.inputs + assert %Reactor.Input{name: :marty} in reactor.inputs end test "when the input has a transformer it adds a transform step and the input" do {:ok, reactor} = add_input(new(), :marty, &String.upcase/1) - assert :marty in reactor.inputs + assert %Reactor.Input{name: :marty} in reactor.inputs [transform_step] = reactor.steps assert transform_step.name == {:__reactor__, :transform, :input, :marty} diff --git a/test/reactor/dsl/input_test.exs b/test/reactor/dsl/input_test.exs index feb889e..c2c2613 100644 --- a/test/reactor/dsl/input_test.exs +++ b/test/reactor/dsl/input_test.exs @@ -58,6 +58,6 @@ defmodule Reactor.Dsl.InputTest do reactor = Reactor.Info.to_struct!(InputDescriptionReactor) - assert %{input: "An example input"} = reactor.input_descriptions + assert ["An example input"] = Enum.map(reactor.inputs, & &1.description) end end