Current section

Files

Jump to
aion_flow src aion@child.erl
Raw

src/aion@child.erl

-module(aion@child).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/aion/child.gleam").
-export([spawn/6, error_codec/1, output_codec/1, child_id/1, await/1, spawn_and_wait/6]).
-export_type([child_handle/2]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(" Typed child-workflow handles and await wrappers.\n").
-opaque child_handle(EQU, EQV) :: {child_handle,
binary(),
aion@codec:codec(EQU),
aion@codec:codec(EQV)}.
-file("src/aion/child.gleam", 169).
-spec spawn_config() -> binary().
spawn_config() ->
_pipe = gleam@json:object([]),
gleam@json:to_string(_pipe).
-file("src/aion/child.gleam", 30).
?DOC(
" Start a linked child workflow and return its typed handle.\n"
"\n"
" The `workflow_fn` is accepted as a type anchor for the child workflow's\n"
" `fn(input) -> Result(output, workflow_error)` contract. The SDK does not call\n"
" it here; lifecycle, linking, recording, and replay/no-respawn behavior are\n"
" owned by AT/AD behind the FFI boundary.\n"
).
-spec spawn(
binary(),
fun((EQW) -> {ok, EQX} | {error, EQY}),
EQW,
aion@codec:codec(EQW),
aion@codec:codec(EQX),
aion@codec:codec(EQY)
) -> {ok, child_handle(EQX, EQY)} | {error, aion@error:engine_error()}.
spawn(Name, Workflow_fn, Input, Input_codec, Output_codec, Error_codec) ->
_ = Workflow_fn,
Encoded_input = (erlang:element(2, Input_codec))(Input),
case aion_flow_ffi:spawn_child(Name, Encoded_input, spawn_config()) of
{ok, Raw_child_id} ->
{ok, {child_handle, Raw_child_id, Output_codec, Error_codec}};
{error, Raw_error} ->
{error, {engine_failure, Raw_error}}
end.
-file("src/aion/child.gleam", 135).
-spec copy_payload_suffix(binary(), integer()) -> binary().
copy_payload_suffix(Raw_result, Prefix_size) ->
_pipe = Raw_result,
_pipe@1 = gleam@string:drop_start(_pipe, Prefix_size),
(fun(Suffix) -> erlang:iolist_to_binary([Suffix]) end)(_pipe@1).
-file("src/aion/child.gleam", 115).
?DOC(" Return the workflow-error codec carried by this child handle.\n").
-spec error_codec(child_handle(any(), ESK)) -> aion@codec:codec(ESK).
error_codec(Handle) ->
erlang:element(4, Handle).
-file("src/aion/child.gleam", 158).
-spec decode_error_payload(binary(), child_handle(ETD, ETE)) -> {ok, ETD} |
{error, aion@error:child_error(ETE)}.
decode_error_payload(Payload, Handle) ->
Codec = error_codec(Handle),
case (erlang:element(3, Codec))(Payload) of
{ok, Workflow_error} ->
{error, {child_workflow_failed, Workflow_error}};
{error, Decode_error} ->
{error, {child_error_decode_failed, Decode_error}}
end.
-file("src/aion/child.gleam", 108).
?DOC(" Return the output codec carried by this child handle.\n").
-spec output_codec(child_handle(ESE, any())) -> aion@codec:codec(ESE).
output_codec(Handle) ->
erlang:element(3, Handle).
-file("src/aion/child.gleam", 147).
-spec decode_output(binary(), child_handle(ESW, ESX)) -> {ok, ESW} |
{error, aion@error:child_error(ESX)}.
decode_output(Payload, Handle) ->
Codec = output_codec(Handle),
case (erlang:element(3, Codec))(Payload) of
{ok, Output} ->
{ok, Output};
{error, Decode_error} ->
{error, {child_output_decode_failed, Decode_error}}
end.
-file("src/aion/child.gleam", 121).
-spec decode_child_result(binary(), child_handle(ESO, ESP)) -> {ok, ESO} |
{error, aion@error:child_error(ESP)}.
decode_child_result(Raw_result, Handle) ->
case gleam_stdlib:string_starts_with(Raw_result, <<"ok:"/utf8>>) of
true ->
decode_output(copy_payload_suffix(Raw_result, 3), Handle);
false ->
case gleam_stdlib:string_starts_with(Raw_result, <<"error:"/utf8>>) of
true ->
decode_error_payload(
copy_payload_suffix(Raw_result, 6),
Handle
);
false ->
{error, {child_engine_failure, Raw_result}}
end
end.
-file("src/aion/child.gleam", 103).
?DOC(" Return the engine child/correlation id carried by this handle.\n").
-spec child_id(child_handle(any(), any())) -> binary().
child_id(Handle) ->
erlang:element(2, Handle).
-file("src/aion/child.gleam", 63).
?DOC(
" Await a child workflow's recorded completion or failure.\n"
"\n"
" AT/AD own blocking, replay resolution, and event recording. This wrapper\n"
" decodes the raw recorded envelope with the codecs carried on the handle and\n"
" returns decode/engine failures as typed data.\n"
"\n"
" The await is a yield point: pending workflow queries are serviced by the\n"
" query pump before the child terminal resolves, exactly as activity awaits,\n"
" signal receives, and timers do. Without the pump, a query arriving while\n"
" the workflow is parked here would surface its sentinel as a bogus child\n"
" failure and leave the engine refusing every later await in the run.\n"
).
-spec await(child_handle(ERI, ERJ)) -> {ok, ERI} |
{error, aion@error:child_error(ERJ)}.
await(Handle) ->
Awaited_child_id = child_id(Handle),
case aion@internal@pump:run(
fun() ->
aion@internal@pump:shield(
aion_flow_ffi:await_child(Awaited_child_id)
)
end
) of
{ok, Raw_result} ->
decode_child_result(Raw_result, Handle);
{error, Raw_error} ->
{error, {child_engine_failure, Raw_error}}
end.
-file("src/aion/child.gleam", 87).
?DOC(
" Start a linked child workflow and await its recorded result.\n"
"\n"
" This is the spawn-then-await convenience kept in the child logic module so\n"
" `aion/workflow` can remain a forwarding authoring surface.\n"
).
-spec spawn_and_wait(
binary(),
fun((ERP) -> {ok, ERQ} | {error, ERR}),
ERP,
aion@codec:codec(ERP),
aion@codec:codec(ERQ),
aion@codec:codec(ERR)
) -> {ok, ERQ} | {error, aion@error:child_error(ERR)}.
spawn_and_wait(Name, Workflow_fn, Input, Input_codec, Output_codec, Error_codec) ->
case spawn(Name, Workflow_fn, Input, Input_codec, Output_codec, Error_codec) of
{ok, Handle} ->
await(Handle);
{error, {engine_failure, Message}} ->
{error, {child_engine_failure, Message}}
end.