Current section

Files

Jump to
glixir src glixir@supervisor.erl
Raw

src/glixir@supervisor.erl

-module(glixir@supervisor).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-define(FILEPATH, "src/glixir/supervisor.gleam").
-export([encode_child_args/1, start_dynamic_supervisor_named/1, child_spec/8, start_dynamic_child/4, terminate_dynamic_child/2, which_dynamic_children/1, count_dynamic_children/1]).
-export_type([dynamic_supervisor/2, child_spec/2, restart_strategy/0, child_type/0, supervisor_error/0, start_child_result/1, child_status/0, child_info/2, child_counts/0, child_operation_error/0, dynamic_supervisor_result/0, dynamic_child_result/0, dynamic_terminate_result/0]).
-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(
" Type-safe, bounded DynamicSupervisor interop for Gleam/Elixir\n"
"\n"
" This module provides a *phantom-typed* interface to Elixir DynamicSupervisor.\n"
" All child processes are parameterized by their `args` and `reply` types,\n"
" so misuse is a compile error, not a runtime surprise.\n"
).
-opaque dynamic_supervisor(FAM, FAN) :: {dynamic_supervisor,
gleam@erlang@process:pid_()} |
{gleam_phantom, FAM, FAN}.
-type child_spec(FAO, FAP) :: {child_spec,
binary(),
gleam@erlang@atom:atom_(),
gleam@erlang@atom:atom_(),
FAO,
restart_strategy(),
integer(),
child_type()} |
{gleam_phantom, FAP}.
-type restart_strategy() :: permanent | temporary | transient.
-type child_type() :: worker | supervisor_child.
-type supervisor_error() :: {start_error, binary()} |
{child_start_error, binary(), binary()} |
{child_not_found, binary()} |
{already_started, binary()} |
{invalid_child_spec, binary()}.
-type start_child_result(FAQ) :: {child_started,
gleam@erlang@process:pid_(),
FAQ} |
{start_child_error, binary()}.
-type child_status() :: {child_pid, gleam@erlang@process:pid_()} |
child_restarting |
child_undefined.
-type child_info(FAR, FAS) :: {child_info,
binary(),
gleam@option:option(gleam@erlang@process:pid_()),
child_type(),
child_status(),
FAR,
FAS}.
-type child_counts() :: {child_counts,
integer(),
integer(),
integer(),
integer()}.
-type child_operation_error() :: running |
restarting |
not_found |
simple_one_for_one |
{other_error, binary()}.
-type dynamic_supervisor_result() :: {dynamic_supervisor_ok,
gleam@erlang@process:pid_()} |
{dynamic_supervisor_error, gleam@dynamic:dynamic_()}.
-type dynamic_child_result() :: {dynamic_start_child_ok,
gleam@erlang@process:pid_()} |
{dynamic_start_child_error, gleam@dynamic:dynamic_()}.
-type dynamic_terminate_result() :: dynamic_terminate_child_ok |
{dynamic_terminate_child_error, gleam@dynamic:dynamic_()}.
-file("src/glixir/supervisor.gleam", 143).
-spec restart_to_string(restart_strategy()) -> binary().
restart_to_string(R) ->
case R of
permanent ->
<<"permanent"/utf8>>;
temporary ->
<<"temporary"/utf8>>;
transient ->
<<"transient"/utf8>>
end.
-file("src/glixir/supervisor.gleam", 151).
-spec child_type_to_string(child_type()) -> binary().
child_type_to_string(T) ->
case T of
worker ->
<<"worker"/utf8>>;
supervisor_child ->
<<"supervisor"/utf8>>
end.
-file("src/glixir/supervisor.gleam", 159).
-spec encode_child_args(any()) -> list(gleam@dynamic:dynamic_()).
encode_child_args(Child_args) ->
[].
-file("src/glixir/supervisor.gleam", 167).
?DOC(" Start a named dynamic supervisor with bounded child types\n").
-spec start_dynamic_supervisor_named(gleam@erlang@atom:atom_()) -> {ok,
dynamic_supervisor(any(), any())} |
{error, supervisor_error()}.
start_dynamic_supervisor_named(Name) ->
glixir@utils:debug_log(
debug,
<<"[supervisor] Starting supervisor: "/utf8,
(erlang:atom_to_binary(Name))/binary>>
),
case 'Elixir.Glixir.Supervisor':start_dynamic_supervisor_named(
erlang:atom_to_binary(Name)
) of
{dynamic_supervisor_ok, Pid} ->
glixir@utils:debug_log(
info,
<<"[supervisor] Supervisor started successfully"/utf8>>
),
{ok, {dynamic_supervisor, Pid}};
{dynamic_supervisor_error, Reason} ->
glixir@utils:debug_log(
error,
<<"[supervisor] Supervisor start failed"/utf8>>
),
{error, {start_error, gleam@string:inspect(Reason)}}
end.
-file("src/glixir/supervisor.gleam", 191).
?DOC(" Build a type-safe child spec\n").
-spec child_spec(
binary(),
binary(),
binary(),
FBC,
restart_strategy(),
integer(),
child_type(),
fun((FBC) -> list(gleam@dynamic:dynamic_()))
) -> child_spec(FBC, any()).
child_spec(
Id,
Module,
Function,
Args,
Restart,
Shutdown_timeout,
Child_type,
Encode
) ->
{child_spec,
Id,
erlang:binary_to_atom(Module),
erlang:binary_to_atom(Function),
Args,
Restart,
Shutdown_timeout,
Child_type}.
-file("src/glixir/supervisor.gleam", 213).
?DOC(" Build a dynamic spec map for Elixir\n").
-spec spec_to_map(
binary(),
gleam@erlang@atom:atom_(),
gleam@erlang@atom:atom_(),
list(gleam@dynamic:dynamic_()),
restart_strategy(),
integer(),
child_type()
) -> gleam@dynamic:dynamic_().
spec_to_map(Id, M, F, Args, Restart, Shutdown, Child_type) ->
gleam@dynamic:properties(
[{gleam_stdlib:identity(<<"id"/utf8>>), gleam_stdlib:identity(Id)},
{gleam_stdlib:identity(<<"start_module"/utf8>>),
gleam_stdlib:identity(erlang:atom_to_binary(M))},
{gleam_stdlib:identity(<<"start_function"/utf8>>),
gleam_stdlib:identity(erlang:atom_to_binary(F))},
{gleam_stdlib:identity(<<"start_args"/utf8>>),
erlang:list_to_tuple(Args)},
{gleam_stdlib:identity(<<"restart"/utf8>>),
gleam_stdlib:identity(restart_to_string(Restart))},
{gleam_stdlib:identity(<<"shutdown"/utf8>>),
gleam_stdlib:identity(erlang:integer_to_binary(Shutdown))},
{gleam_stdlib:identity(<<"type"/utf8>>),
gleam_stdlib:identity(child_type_to_string(Child_type))}]
).
-file("src/glixir/supervisor.gleam", 234).
?DOC(" Start a child with typed args and replies\n").
-spec start_dynamic_child(
dynamic_supervisor(FBI, FBJ),
child_spec(FBI, FBJ),
fun((FBI) -> list(gleam@dynamic:dynamic_())),
fun((gleam@dynamic:dynamic_()) -> {ok, FBJ} | {error, binary()})
) -> start_child_result(FBJ).
start_dynamic_child(Supervisor, Spec, Encode, Decode) ->
{dynamic_supervisor, Pid} = Supervisor,
{child_spec, Id, M, F, Args, Restart, Timeout, Type_} = Spec,
glixir@utils:debug_log(
debug,
<<"[supervisor] Starting child: "/utf8, Id/binary>>
),
Spec_map = spec_to_map(Id, M, F, Encode(Args), Restart, Timeout, Type_),
case 'Elixir.Glixir.Supervisor':start_dynamic_child(Pid, Spec_map) of
{dynamic_start_child_ok, Child_pid} ->
case Decode(gleam_stdlib:identity(<<"ok"/utf8>>)) of
{ok, Child_reply} ->
glixir@utils:debug_log(
info,
<<"[supervisor] Child started successfully: "/utf8,
Id/binary>>
),
{child_started, Child_pid, Child_reply};
{error, E} ->
glixir@utils:debug_log(
error,
<<"[supervisor] Child decode failed: "/utf8, E/binary>>
),
{start_child_error, E}
end;
{dynamic_start_child_error, Reason} ->
glixir@utils:debug_log(
error,
<<"[supervisor] Child start failed: "/utf8, Id/binary>>
),
{start_child_error, gleam@string:inspect(Reason)}
end.
-file("src/glixir/supervisor.gleam", 272).
?DOC(" Terminate a child (by pid)\n").
-spec terminate_dynamic_child(
dynamic_supervisor(any(), any()),
gleam@erlang@process:pid_()
) -> {ok, nil} | {error, binary()}.
terminate_dynamic_child(Supervisor, Child_pid) ->
{dynamic_supervisor, Pid} = Supervisor,
glixir@utils:debug_log(
debug,
<<"[supervisor] Terminating child: "/utf8,
(gleam@string:inspect(Child_pid))/binary>>
),
Ffi_result = 'Elixir.Glixir.Supervisor':terminate_dynamic_child(
Pid,
Child_pid
),
glixir@utils:debug_log(
debug,
<<"[supervisor] FFI result: "/utf8,
(gleam@string:inspect(Ffi_result))/binary>>
),
Result_string = gleam@string:inspect(Ffi_result),
case Result_string of
<<"DynamicTerminateChildOk()"/utf8>> ->
glixir@utils:debug_log(
info,
<<"[supervisor] Child terminated successfully"/utf8>>
),
{ok, nil};
_ ->
glixir@utils:debug_log(
error,
<<"[supervisor] Child termination failed"/utf8>>
),
{error,
<<"FFI returned unexpected result: "/utf8,
Result_string/binary>>}
end.
-file("src/glixir/supervisor.gleam", 308).
?DOC(
" Returns all dynamic children as a list of Dynamic.\n"
" You might want to write a real decoder for full type-safety!\n"
).
-spec which_dynamic_children(dynamic_supervisor(any(), any())) -> list(gleam@dynamic:dynamic_()).
which_dynamic_children(Supervisor) ->
{dynamic_supervisor, Pid} = Supervisor,
glixir@utils:debug_log(debug, <<"[supervisor] Querying children"/utf8>>),
Result = 'Elixir.Glixir.Supervisor':which_dynamic_children(Pid),
glixir@utils:debug_log(
debug,
<<<<"[supervisor] Found "/utf8,
(erlang:integer_to_binary(erlang:length(Result)))/binary>>/binary,
" children"/utf8>>
),
Result.
-file("src/glixir/supervisor.gleam", 321).
-spec child_counts_decoder() -> gleam@dynamic@decode:decoder(child_counts()).
child_counts_decoder() ->
gleam@dynamic@decode:field(
<<"specs"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_int/1},
fun(Specs) ->
gleam@dynamic@decode:field(
<<"active"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_int/1},
fun(Active) ->
gleam@dynamic@decode:field(
<<"supervisors"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_int/1},
fun(Supervisors) ->
gleam@dynamic@decode:field(
<<"workers"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_int/1},
fun(Workers) ->
gleam@dynamic@decode:success(
{child_counts,
Specs,
Active,
Supervisors,
Workers}
)
end
)
end
)
end
)
end
).
-file("src/glixir/supervisor.gleam", 330).
-spec count_dynamic_children(dynamic_supervisor(any(), any())) -> {ok,
child_counts()} |
{error, binary()}.
count_dynamic_children(Supervisor) ->
Child_counts_decoder = begin
gleam@dynamic@decode:field(
<<"specs"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_int/1},
fun(Specs) ->
gleam@dynamic@decode:field(
<<"active"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_int/1},
fun(Active) ->
gleam@dynamic@decode:field(
<<"supervisors"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_int/1},
fun(Supervisors) ->
gleam@dynamic@decode:field(
<<"workers"/utf8>>,
{decoder,
fun gleam@dynamic@decode:decode_int/1},
fun(Workers) ->
gleam@dynamic@decode:success(
{child_counts,
Specs,
Active,
Supervisors,
Workers}
)
end
)
end
)
end
)
end
)
end,
{dynamic_supervisor, Pid} = Supervisor,
glixir@utils:debug_log(debug, <<"[supervisor] Counting children"/utf8>>),
Result_dynamic = 'Elixir.Glixir.Supervisor':count_dynamic_children(Pid),
case gleam@dynamic@decode:run(Result_dynamic, Child_counts_decoder) of
{ok, Counts} ->
glixir@utils:debug_log(
debug,
<<"[supervisor] Child counts retrieved successfully"/utf8>>
),
{ok, Counts};
{error, Errors} ->
glixir@utils:debug_log(
error,
<<"[supervisor] Child count decode failed"/utf8>>
),
{error,
<<"decode error: "/utf8, (gleam@string:inspect(Errors))/binary>>}
end.