Current section
Files
Jump to
Current section
Files
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([start_dynamic_supervisor_named/1, start_dynamic_supervisor_named_safe/1, child_spec/7, start_dynamic_child/4, terminate_dynamic_child/2, which_dynamic_children/1, count_dynamic_children/1, get_all_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, get_all_children_result/1]).
-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(FCE, FCF) :: {dynamic_supervisor,
gleam@erlang@process:pid_()} |
{gleam_phantom, FCE, FCF}.
-type child_spec(FCG, FCH) :: {child_spec,
binary(),
gleam@erlang@atom:atom_(),
gleam@erlang@atom:atom_(),
FCG,
restart_strategy(),
integer(),
child_type()} |
{gleam_phantom, FCH}.
-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(FCI) :: {child_started,
gleam@erlang@process:pid_(),
FCI} |
{start_child_error, binary()}.
-type child_status() :: {child_pid, gleam@erlang@process:pid_()} |
child_restarting |
child_undefined.
-type child_info(FCJ, FCK) :: {child_info,
binary(),
gleam@option:option(gleam@erlang@process:pid_()),
child_type(),
child_status(),
FCJ,
FCK}.
-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_()}.
-type get_all_children_result(FCL) :: {get_all_children_ok,
list(gleam@erlang@process:subject(FCL))} |
{get_all_children_error, gleam@dynamic:dynamic_()}.
-file("src/glixir/supervisor.gleam", 151).
-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", 159).
-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", 169).
?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", 194).
?DOC(
" SAFE: Start a named dynamic supervisor with string name (no atom creation)\n"
" Use this for user-generated names to avoid atom table exhaustion\n"
).
-spec start_dynamic_supervisor_named_safe(binary()) -> {ok,
dynamic_supervisor(any(), any())} |
{error, supervisor_error()}.
start_dynamic_supervisor_named_safe(Name) ->
glixir@utils:debug_log(
debug,
<<"[supervisor] Starting supervisor (safe): "/utf8, Name/binary>>
),
case 'Elixir.Glixir.Supervisor':start_dynamic_supervisor_named(Name) of
{dynamic_supervisor_ok, Pid} ->
glixir@utils:debug_log(
info,
<<"[supervisor] Supervisor started successfully (safe)"/utf8>>
),
{ok, {dynamic_supervisor, Pid}};
{dynamic_supervisor_error, Reason} ->
glixir@utils:debug_log(
error,
<<"[supervisor] Supervisor start failed (safe)"/utf8>>
),
{error, {start_error, gleam@string:inspect(Reason)}}
end.
-file("src/glixir/supervisor.gleam", 221).
?DOC(" Build a type-safe child spec\n").
-spec child_spec(
binary(),
binary(),
binary(),
FDB,
restart_strategy(),
integer(),
child_type()
) -> child_spec(FDB, any()).
child_spec(Id, Module, Function, Args, Restart, Shutdown_timeout, Child_type) ->
{child_spec,
Id,
erlang:binary_to_atom(Module),
erlang:binary_to_atom(Function),
Args,
Restart,
Shutdown_timeout,
Child_type}.
-file("src/glixir/supervisor.gleam", 242).
?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", 263).
?DOC(" Start a child with typed args and replies\n").
-spec start_dynamic_child(
dynamic_supervisor(FDG, FDH),
child_spec(FDG, FDH),
fun((FDG) -> list(gleam@dynamic:dynamic_())),
fun((gleam@dynamic:dynamic_()) -> {ok, FDH} | {error, binary()})
) -> start_child_result(FDH).
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", 301).
?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", 337).
?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", 350).
-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.
-file("src/glixir/supervisor.gleam", 379).
-spec get_all_dynamic_children(dynamic_supervisor(any(), FEI)) -> {ok,
list(gleam@erlang@process:subject(FEI))} |
{error, binary()}.
get_all_dynamic_children(Supervisor) ->
{dynamic_supervisor, Pid} = Supervisor,
glixir@utils:debug_log(
debug,
<<"[supervisor] Getting all child PIDs"/utf8>>
),
case 'Elixir.Glixir.Supervisor':get_all_dynamic_children(Pid) of
{get_all_children_ok, Pids} ->
glixir@utils:debug_log(
debug,
<<<<"[supervisor] Retrieved "/utf8,
(erlang:integer_to_binary(erlang:length(Pids)))/binary>>/binary,
" child PIDs"/utf8>>
),
{ok, Pids};
{get_all_children_error, Reason} ->
glixir@utils:debug_log(
error,
<<"[supervisor] Failed to get children"/utf8>>
),
{error, gleam@string:inspect(Reason)}
end.