Packages

ALF-backed stepwise and flow workflow engine

Current section

Files

Jump to
mobus_stepwise lib mobus stepwise ir.ex
Raw

lib/mobus/stepwise/ir.ex

defmodule Mobus.Stepwise.IR do
@moduledoc """
Normalization helpers for workflow specs compiled into IR.
Normalizes spec shapes to reduce downstream conditionals in the
stepwise engine and pipeline components.
"""
@type t :: map()
@doc """
Normalizes a raw workflow spec into the internal representation (IR).
Applies the following normalizations:
- Coerces `"stepwise"` / `"flow"` string profiles to atoms
- Extracts `:initial_state` from atom or string keys
- Ensures `:states`, `:transitions`, and `:nodes` are maps (converts keyword lists)
This reduces downstream conditionals in engine and pipeline components.
## Parameters
* `spec` — a raw workflow specification map
## Returns
* A normalized spec map (IR) with consistent atom keys.
## Examples
IR.normalize(%{"profile" => "stepwise", "initial_state" => :step_one, "states" => %{}})
#=> %{profile: :stepwise, initial_state: :step_one, states: %{}, transitions: %{}, ...}
"""
@spec normalize(map()) :: t()
def normalize(%{} = spec) do
spec
|> normalize_profile()
|> normalize_initial_state()
|> normalize_states()
|> normalize_transitions()
|> normalize_nodes()
|> normalize_edges()
end
defp normalize_profile(spec) do
profile = Map.get(spec, :profile) || Map.get(spec, "profile")
profile =
case profile do
"stepwise" -> :stepwise
"flow" -> :flow
other -> other
end
Map.put(spec, :profile, profile)
end
defp normalize_initial_state(spec) do
initial = Map.get(spec, :initial_state) || Map.get(spec, "initial_state")
Map.put(spec, :initial_state, initial)
end
defp normalize_states(spec) do
states = Map.get(spec, :states) || Map.get(spec, "states") || %{}
Map.put(spec, :states, states)
end
defp normalize_transitions(spec) do
transitions = Map.get(spec, :transitions) || Map.get(spec, "transitions") || %{}
transitions =
case transitions do
%{} -> transitions
list when is_list(list) -> Map.new(list, fn {k, v} -> {k, v} end)
other -> other
end
Map.put(spec, :transitions, transitions)
end
defp normalize_nodes(spec) do
nodes = Map.get(spec, :nodes) || Map.get(spec, "nodes") || %{}
nodes =
case nodes do
%{} -> nodes
list when is_list(list) -> Map.new(list, fn {k, v} -> {k, v} end)
other -> other
end
Map.put(spec, :nodes, nodes)
end
defp normalize_edges(spec) do
edges = Map.get(spec, :edges) || Map.get(spec, "edges") || []
Map.put(spec, :edges, edges)
end
end