-module(process_file). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/process_file.gleam"). -export([start/0, supervised/0, register/2]). -export_type([process_file_manager/0, message/0, state/0, do_not_leak/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. -opaque process_file_manager() :: {process_file_manager, mala:bag_table(gleam@erlang@process:pid_(), binary()), gleam@erlang@process:subject(message())}. -opaque message() :: {registration, binary(), gleam@erlang@process:subject(nil)} | {monitored_process_down, gleam@erlang@process:pid_()}. -type state() :: {state, mala:bag_table(gleam@erlang@process:pid_(), binary())}. -type do_not_leak() :: any(). -file("src/process_file.gleam", 93). -spec handle_registration(state(), binary(), gleam@erlang@process:subject(nil)) -> gleam@otp@actor:next(state(), message()). handle_registration(State, Path, Reply) -> Pid@1 = case gleam@erlang@process:subject_owner(Reply) of {ok, Pid} -> Pid; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"registee must be alive"/utf8>>, file => <>, module => <<"process_file"/utf8>>, function => <<"handle_registration"/utf8>>, line => 99, value => _assert_fail, start => 3083, 'end' => 3132, pattern_start => 3094, pattern_end => 3101}) end, _ = gleam@erlang@process:monitor(Pid@1), case mala_ffi:bag_insert(erlang:element(2, State), Pid@1, Path) of {ok, _} -> nil; _assert_fail@1 -> erlang:error(#{gleam_error => let_assert, message => <<"ETS table exists"/utf8>>, file => <>, module => <<"process_file"/utf8>>, function => <<"handle_registration"/utf8>>, line => 105, value => _assert_fail@1, start => 3393, 'end' => 3447, pattern_start => 3404, pattern_end => 3409}) end, gleam@erlang@process:send(Reply, nil), gleam@otp@actor:continue(State). -file("src/process_file.gleam", 130). -spec delete_file(binary()) -> nil. delete_file(Path) -> case simplifile_erl:delete(Path) of {ok, nil} -> nil; {error, enoent} -> nil; {error, Error} -> logger:error( [{erlang:binary_to_atom(<<"message"/utf8>>), <<"process_file failed to delete file"/utf8>>}, {erlang:binary_to_atom(<<"cause"/utf8>>), simplifile:describe_error(Error)}, {erlang:binary_to_atom(<<"path"/utf8>>), Path}] ), nil end. -file("src/process_file.gleam", 114). -spec handle_monitored_process_down(state(), gleam@erlang@process:pid_()) -> gleam@otp@actor:next(state(), message()). handle_monitored_process_down(State, Pid) -> Table = erlang:element(2, State), Paths@1 = case mala_ffi:bag_get(Table, Pid) of {ok, Paths} -> Paths; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"ETS table exists"/utf8>>, file => <>, module => <<"process_file"/utf8>>, function => <<"handle_monitored_process_down"/utf8>>, line => 120, value => _assert_fail, start => 3790, 'end' => 3833, pattern_start => 3801, pattern_end => 3810}) end, case mala_ffi:bag_delete_key(Table, Pid) of {ok, _} -> nil; _assert_fail@1 -> erlang:error(#{gleam_error => let_assert, message => <<"ETS table exists"/utf8>>, file => <>, module => <<"process_file"/utf8>>, function => <<"handle_monitored_process_down"/utf8>>, line => 121, value => _assert_fail@1, start => 3858, 'end' => 3904, pattern_start => 3869, pattern_end => 3874}) end, gleam@list:each(Paths@1, fun delete_file/1), gleam@otp@actor:continue(State). -file("src/process_file.gleam", 83). -spec handle_message(state(), message()) -> gleam@otp@actor:next(state(), message()). handle_message(State, Message) -> case Message of {monitored_process_down, Pid} -> handle_monitored_process_down(State, Pid); {registration, Path, Reply} -> handle_registration(State, Path, Reply) end. -file("src/process_file.gleam", 171). -spec convert_down_message(gleam@erlang@process:down()) -> message(). convert_down_message(Down) -> Pid@1 = case Down of {process_down, _, Pid, _} -> Pid; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"process_file manager actor should never be monitoring a port"/utf8>>, file => <>, module => <<"process_file"/utf8>>, function => <<"convert_down_message"/utf8>>, line => 172, value => _assert_fail, start => 5135, 'end' => 5182, pattern_start => 5146, pattern_end => 5175}) end, {monitored_process_down, Pid@1}. -file("src/process_file.gleam", 155). -spec initialise(gleam@erlang@process:subject(message())) -> {ok, gleam@otp@actor:initialised(state(), message(), process_file_manager())} | {error, any()}. initialise(Subject) -> Table = mala:new(), Handle = {process_file_manager, Table, Subject}, Selector = begin _pipe = gleam_erlang_ffi:new_selector(), _pipe@1 = gleam@erlang@process:select(_pipe, Subject), gleam@erlang@process:select_monitors( _pipe@1, fun convert_down_message/1 ) end, _pipe@2 = gleam@otp@actor:initialised({state, Table}), _pipe@3 = gleam@otp@actor:selecting(_pipe@2, Selector), _pipe@4 = gleam@otp@actor:returning(_pipe@3, Handle), {ok, _pipe@4}. -file("src/process_file.gleam", 26). ?DOC( " Create a new process file manager. Typically you should use the\n" " `supervised` function instead of this one, adding it to your application's\n" " supervision tree.\n" ). -spec start() -> {ok, gleam@otp@actor:started(process_file_manager())} | {error, gleam@otp@actor:start_error()}. start() -> _pipe = gleam@otp@actor:new_with_initialiser(1000, fun initialise/1), _pipe@1 = gleam@otp@actor:on_message(_pipe, fun handle_message/2), gleam@otp@actor:start(_pipe@1). -file("src/process_file.gleam", 34). ?DOC(" A supervision specification for a new process file manager.\n"). -spec supervised() -> gleam@otp@supervision:child_specification(process_file_manager()). supervised() -> gleam@otp@supervision:worker(fun start/0). -file("src/process_file.gleam", 50). ?DOC( " Register a file system path as being owned by the process that calls this\n" " function, so when the process exits the process file manager will delete\n" " any file or directory at the path. If there is no file at that location\n" " then nothing happens.\n" "\n" " If the file could not be deleted (such as due to file permissions not\n" " allowing it) then the error is logged.\n" "\n" " The first time a process calls this function they will be registered by the\n" " manager process, but subsequent calls work directly with ETS and do not\n" " produce any work for the manager.\n" ). -spec register(process_file_manager(), binary()) -> {ok, nil} | {error, nil}. register(Manager, New_path) -> Owner = erlang:self(), Table = erlang:element(2, Manager), gleam@result:'try'( mala_ffi:bag_has_key(Table, Owner), fun(Already_registered) -> case Already_registered of true -> mala_ffi:bag_insert(Table, Owner, New_path); false -> {ok, gleam@otp@actor:call( erlang:element(3, Manager), 1000, fun(_capture) -> {registration, New_path, _capture} end )} end end ).