-module(gleam@otp@supervisor). -compile(no_auto_import). -export([add/2, supervisor/1, worker/1, returning/2, start_spec/1, start/1, application_stopped/0, to_erlang_start_result/1, from_erlang_start_result/1, wrap_erlang_starter/1]). -export_type([spec/2, children/1, child_spec/3, child_start_error/0, message/0, instruction/0, state/1, starter/1, child/1, handle_exit_error/0, application_start_mode/0, application_stop/0]). -type spec(FEQ, FER) :: {spec, FEQ, integer(), integer(), fun((children(FEQ)) -> children(FER))}. -opaque children(FES) :: {ready, starter(FES)} | {failed, child_start_error()}. -opaque child_spec(FET, FEU, FEV) :: {child_spec, fun((FEU) -> {ok, gleam@otp@process:sender(FET)} | {error, gleam@otp@actor:start_error()}), fun((FEU, gleam@otp@process:sender(FET)) -> FEV)}. -type child_start_error() :: {child_start_error, gleam@option:option(gleam@otp@process:pid_()), gleam@otp@actor:start_error()}. -opaque message() :: {exit, gleam@otp@process:exit()} | {retry_restart, gleam@otp@process:pid_()}. -type instruction() :: start_all | {start_from, gleam@otp@process:pid_()}. -type state(FEW) :: {state, gleam@otp@intensity_tracker:intensity_tracker(), starter(FEW), gleam@otp@process:sender(gleam@otp@process:pid_())}. -type starter(FEX) :: {starter, FEX, gleam@option:option(fun((instruction()) -> {ok, {starter(FEX), instruction()}} | {error, child_start_error()}))}. -type child(FEY) :: {child, gleam@otp@process:pid_(), FEY}. -type handle_exit_error() :: {restart_failed, gleam@otp@process:pid_(), gleam@otp@intensity_tracker:intensity_tracker()} | too_many_restarts. -type application_start_mode() :: normal | {takeover, gleam@otp@node:node_()} | {failover, gleam@otp@node:node_()}. -type application_stop() :: any(). -spec start_child(child_spec(any(), FGB, FGC), FGB) -> {ok, child(FGC)} | {error, child_start_error()}. start_child(Child_spec, Argument) -> case begin _pipe = (erlang:element(2, Child_spec))(Argument), gleam@result:map_error( _pipe, fun(_capture) -> {child_start_error, none, _capture} end ) end of {error, _try} -> {error, _try}; {ok, Channel} -> {ok, {child, gleam@otp@process:pid(Channel), (erlang:element(3, Child_spec))(Argument, Channel)}} end. -spec shutdown_child(gleam@otp@process:pid_(), child_spec(any(), any(), any())) -> nil. shutdown_child(Pid, _) -> gleam@otp@process:send_exit(Pid, normal). -spec perform_instruction_for_child( FGP, instruction(), child_spec(any(), FGP, FGR), child(FGR) ) -> {ok, {child(FGR), instruction()}} | {error, child_start_error()}. perform_instruction_for_child(Argument, Instruction, Child_spec, Child) -> Current = erlang:element(2, Child), case Instruction of {start_from, Target} when Target =/= Current -> {ok, {Child, Instruction}}; _@1 -> shutdown_child(Current, Child_spec), case start_child(Child_spec, Argument) of {error, _try} -> {error, _try}; {ok, Child@1} -> {ok, {Child@1, start_all}} end end. -spec add_child_to_starter( starter(FGZ), child_spec(any(), FGZ, FHC), child(FHC) ) -> starter(FHC). add_child_to_starter(Starter, Child_spec, Child) -> Starter@3 = fun(Instruction) -> case case erlang:element(3, Starter) of {some, Start} -> Start(Instruction); none -> {ok, {Starter, Instruction}} end of {error, _try} -> {error, _try}; {ok, {Starter@1, Instruction@1}} -> case perform_instruction_for_child( erlang:element(2, Starter@1), Instruction@1, Child_spec, Child ) of {error, _try@1} -> {error, _try@1}; {ok, {Child@1, Instruction@2}} -> Starter@2 = add_child_to_starter( Starter@1, Child_spec, Child@1 ), {ok, {Starter@2, Instruction@2}} end end end, {starter, erlang:element(3, Child), {some, Starter@3}}. -spec start_and_add_child(starter(FHI), child_spec(any(), FHI, FHL)) -> children(FHL). start_and_add_child(State, Child_spec) -> case start_child(Child_spec, erlang:element(2, State)) of {ok, Child} -> {ready, add_child_to_starter(State, Child_spec, Child)}; {error, Reason} -> {failed, Reason} end. -spec add(children(FHQ), child_spec(any(), FHQ, FHT)) -> children(FHT). add(Children, Child_spec) -> case Children of {failed, Fail} -> {failed, Fail}; {ready, State} -> start_and_add_child(State, Child_spec) end. -spec supervisor( fun((FHY) -> {ok, gleam@otp@process:sender(FHZ)} | {error, gleam@otp@actor:start_error()}) ) -> child_spec(FHZ, FHY, FHY). supervisor(Start) -> {child_spec, Start, fun(Argument, _) -> Argument end}. -spec worker( fun((FIG) -> {ok, gleam@otp@process:sender(FIH)} | {error, gleam@otp@actor:start_error()}) ) -> child_spec(FIH, FIG, FIG). worker(Start) -> {child_spec, Start, fun(Argument, _) -> Argument end}. -spec returning( child_spec(FIO, FIP, any()), fun((FIP, gleam@otp@process:sender(FIO)) -> FIV) ) -> child_spec(FIO, FIP, FIV). returning(Child, Updater) -> {child_spec, erlang:element(2, Child), Updater}. -spec init(spec(any(), FJA)) -> gleam@otp@actor:init_result(state(FJA), message()). init(Spec) -> {Retry_sender, Retry_receiver} = gleam@otp@process:new_channel(), Retry_receiver@1 = gleam@otp@process:map_receiver( Retry_receiver, fun(A) -> {retry_restart, A} end ), Exit_receiver = begin _pipe = gleam@otp@process:trap_exits(), gleam@otp@process:map_receiver(_pipe, fun(A) -> {exit, A} end) end, Receiver = begin _pipe@1 = Exit_receiver, gleam@otp@process:merge_receiver(_pipe@1, Retry_receiver@1) end, Result = begin _pipe@2 = {starter, erlang:element(2, Spec), none}, _pipe@3 = {ready, _pipe@2}, (erlang:element(5, Spec))(_pipe@3) end, case Result of {ready, Starter} -> Restarts = gleam@otp@intensity_tracker:new( erlang:element(3, Spec), erlang:element(4, Spec) ), State = {state, Restarts, Starter, Retry_sender}, {ready, State, {some, Receiver}}; {failed, Reason} -> Reason@1 = {abnormal, gleam@dynamic:from(Reason)}, {failed, Reason@1} end. -spec handle_exit(gleam@otp@process:pid_(), state(FJG)) -> gleam@otp@actor:next(state(FJG)). handle_exit(Pid, State) -> Outcome = begin {some, Start@1} = case erlang:element(3, erlang:element(3, State)) of {some, Start} -> {some, Start}; _try -> erlang:error(#{gleam_error => assert, message => <<"Assertion pattern match failed"/utf8>>, value => _try, module => <<"gleam/otp/supervisor"/utf8>>, function => <<"handle_exit"/utf8>>, line => 289}) end, case begin _pipe = erlang:element(2, State), _pipe@1 = gleam@otp@intensity_tracker:add_event(_pipe), gleam@result:map_error(_pipe@1, fun(_) -> too_many_restarts end) end of {error, _try@1} -> {error, _try@1}; {ok, Restarts} -> case begin _pipe@2 = Start@1({start_from, Pid}), gleam@result:map_error( _pipe@2, fun(E) -> {restart_failed, gleam@option:unwrap(erlang:element(2, E), Pid), Restarts} end ) end of {error, _try@2} -> {error, _try@2}; {ok, {Starter, _@1}} -> {ok, erlang:setelement( 2, erlang:setelement(3, State, Starter), Restarts )} end end end, case Outcome of {ok, State@1} -> {continue, State@1}; {error, {restart_failed, Failed_child, Restarts@1}} -> gleam@otp@process:send(erlang:element(4, State), Failed_child), State@2 = erlang:setelement(2, State, Restarts@1), {continue, State@2}; {error, too_many_restarts} -> {stop, {abnormal, gleam@dynamic:from(too_many_restarts)}} end. -spec loop(message(), state(FJK)) -> gleam@otp@actor:next(state(FJK)). loop(Message, State) -> case Message of {exit, {exit, Pid, _@1}} -> handle_exit(Pid, State); {retry_restart, Pid@1} -> handle_exit(Pid@1, State) end. -spec start_spec(spec(any(), any())) -> {ok, gleam@otp@process:sender(message())} | {error, gleam@otp@actor:start_error()}. start_spec(Spec) -> gleam@otp@actor:start_spec( {spec, fun() -> init(Spec) end, 60000, fun loop/2} ). -spec start(fun((children(nil)) -> children(any()))) -> {ok, gleam@otp@process:sender(message())} | {error, gleam@otp@actor:start_error()}. start(Init) -> start_spec({spec, nil, 5, 1, Init}). -spec application_stopped() -> application_stop(). application_stopped() -> gleam_otp_external:application_stopped(). -spec to_erlang_start_result( {ok, gleam@otp@process:sender(any())} | {error, gleam@otp@actor:start_error()} ) -> {ok, gleam@otp@process:pid_()} | {error, gleam@dynamic:dynamic()}. to_erlang_start_result(Res) -> gleam@otp@actor:to_erlang_start_result(Res). -spec from_erlang_start_result({ok, gleam@otp@process:pid_()} | {error, any()}) -> {ok, gleam@otp@process:sender(any())} | {error, gleam@otp@actor:start_error()}. from_erlang_start_result(Start) -> gleam@otp@actor:from_erlang_start_result(Start). -spec wrap_erlang_starter( fun(() -> {ok, gleam@otp@process:pid_()} | {error, any()}) ) -> fun(() -> {ok, gleam@otp@process:sender(any())} | {error, gleam@otp@actor:start_error()}). wrap_erlang_starter(Start) -> gleam@otp@actor:wrap_erlang_starter(Start).