defimpl Runic.Transmutable, for: Atom do
@moduledoc """
Transmutable protocol implementation for atoms.
When the atom is a Jido Action module (detected via `__action_metadata__/0`),
it is transmuted into a `Jido.Runic.ActionNode`. This enables the ergonomic:
Workflow.new()
|> Workflow.add(MyAction)
|> Workflow.add(OtherAction, to: :my_action)
Non-action atoms fall through to the default `Any` behavior (constant step).
"""
alias Jido.Runic.ActionNode
def transmute(atom), do: to_workflow(atom)
def to_workflow(atom) do
if jido_action?(atom) do
node = ActionNode.new(atom)
Runic.Workflow.new(name: node.name)
|> Runic.Workflow.add(node)
else
Runic.Transmutable.Any.to_workflow(atom)
end
end
def to_component(atom) do
if jido_action?(atom) do
ActionNode.new(atom)
else
Runic.Transmutable.Any.to_component(atom)
end
end
defp jido_action?(mod) when is_atom(mod) do
case :code.is_loaded(mod) do
{:file, _} ->
function_exported?(mod, :__action_metadata__, 0)
false ->
beam_exports_action_metadata?(mod)
end
end
defp beam_exports_action_metadata?(mod) do
case :code.which(mod) do
:non_existing ->
false
:preloaded ->
false
:cover_compiled ->
false
beam_path when is_list(beam_path) ->
case :beam_lib.chunks(beam_path, [:exports]) do
{:ok, {^mod, [exports: exports]}} ->
Enum.member?(exports, {:__action_metadata__, 0})
_ ->
false
end
end
end
end