-module(filespy). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -export([new/0, add_dir/2, add_dirs/2, set_actor_handler/2, set_initial_state/2, set_handler/2, set_initializer/2, selector/0, spec/1, start/1]). -export_type([no_directories/0, has_directories/0, no_handler/0, has_handler/0, no_initial_state/0, has_initial_state/0, event/0, change/1, builder/5]). -type no_directories() :: any(). -type has_directories() :: any(). -type no_handler() :: any(). -type has_handler() :: any(). -type no_initial_state() :: any(). -type has_initial_state() :: any(). -type event() :: created | modified | closed | deleted | renamed | attribute | {unknown, gleam@erlang@atom:atom_()}. -type change(HEN) :: {change, binary(), list(event())} | {custom, HEN}. -opaque builder(HEO, HEP, HEQ, HER, HES) :: {builder, list(binary()), gleam@option:option(fun((change(HES), HEO) -> gleam@otp@actor:next(change(HES), HEO))), gleam@option:option(fun(() -> HEO))} | {gleam_phantom, HEP, HEQ, HER}. -spec new() -> builder(any(), no_directories(), no_handler(), no_initial_state(), nil). new() -> {builder, [], none, none}. -spec add_dir(builder(HFH, any(), HFJ, HFK, HFL), binary()) -> builder(HFH, has_directories(), HFJ, HFK, HFL). add_dir(Builder, Directory) -> {builder, [Directory | erlang:element(2, Builder)], erlang:element(3, Builder), erlang:element(4, Builder)}. -spec add_dirs(builder(HFW, any(), HFY, HFZ, HGA), list(binary())) -> builder(HFW, has_directories(), HFY, HFZ, HGA). add_dirs(Builder, Directories) -> {builder, lists:append(Directories, erlang:element(2, Builder)), erlang:element(3, Builder), erlang:element(4, Builder)}. -spec set_actor_handler( builder(HGY, HGZ, no_handler(), HHA, HHB), fun((change(HHB), HGY) -> gleam@otp@actor:next(change(HHB), HGY)) ) -> builder(HGY, HGZ, has_handler(), HHA, HHB). set_actor_handler(Builder, Handler) -> {builder, erlang:element(2, Builder), {some, Handler}, erlang:element(4, Builder)}. -spec set_initial_state(builder(HHO, HHP, HHQ, no_initial_state(), HHR), HHO) -> builder(HHO, HHP, HHQ, has_initial_state(), HHR). set_initial_state(Builder, State) -> {builder, erlang:element(2, Builder), erlang:element(3, Builder), {some, fun() -> State end}}. -spec set_handler( builder(nil, HGM, no_handler(), no_initial_state(), HGN), fun((binary(), event()) -> nil) ) -> builder(nil, HGM, has_handler(), has_initial_state(), HGN). set_handler(Builder, Handler) -> Wrapped_handler = fun(Event, _) -> case Event of {change, Path, Events} -> gleam@list:each(Events, fun(Ev) -> Handler(Path, Ev) end); _ -> nil end, gleam@otp@actor:continue(nil) end, _pipe = Builder, _pipe@1 = set_actor_handler(_pipe, Wrapped_handler), set_initial_state(_pipe@1, nil). -spec set_initializer( builder(HIC, HID, HIE, no_initial_state(), HIF), fun(() -> HIC) ) -> builder(HIC, HID, HIE, has_initial_state(), HIF). set_initializer(Builder, Initializer) -> {builder, erlang:element(2, Builder), erlang:element(3, Builder), {some, Initializer}}. -spec to_event(gleam@dynamic:dynamic_()) -> {ok, event()} | {error, list(gleam@dynamic:decode_error())}. to_event(From) -> gleam@result:'try'( gleam_erlang_ffi:atom_from_dynamic(From), fun(Event) -> Created = erlang:binary_to_atom(<<"created"/utf8>>), Deleted = erlang:binary_to_atom(<<"deleted"/utf8>>), Modified = erlang:binary_to_atom(<<"modified"/utf8>>), Closed = erlang:binary_to_atom(<<"closed"/utf8>>), Renamed = erlang:binary_to_atom(<<"renamed"/utf8>>), Attrib = erlang:binary_to_atom(<<"attribute"/utf8>>), Removed = erlang:binary_to_atom(<<"removed"/utf8>>), case Event of Ev when Ev =:= Created -> {ok, created}; Ev@1 when Ev@1 =:= Deleted -> {ok, deleted}; Ev@2 when Ev@2 =:= Modified -> {ok, modified}; Ev@3 when Ev@3 =:= Closed -> {ok, closed}; Ev@4 when Ev@4 =:= Renamed -> {ok, renamed}; Ev@5 when Ev@5 =:= Removed -> {ok, deleted}; Ev@6 when Ev@6 =:= Attrib -> {ok, attribute}; Other -> {ok, {unknown, Other}} end end ). -spec selector() -> gleam@erlang@process:selector(change(any())). selector() -> Change_selector = gleam@dynamic:tuple3( fun gleam@dynamic:dynamic/1, fun gleam@dynamic:dynamic/1, gleam@dynamic:tuple2(fun(L) -> _pipe = filespy_ffi:identity(L), _pipe@1 = unicode:characters_to_binary(_pipe), {ok, _pipe@1} end, gleam@dynamic:list(fun to_event/1)) ), _pipe@2 = gleam_erlang_ffi:new_selector(), gleam@erlang@process:selecting_anything( _pipe@2, fun(Event) -> case Change_selector(Event) of {ok, {_, _, {Path, Events}}} -> {change, Path, Events}; _ -> {change, <<""/utf8>>, []} end end ). -spec spec( builder(HIW, has_directories(), has_handler(), has_initial_state(), HIX) ) -> gleam@otp@actor:spec(HIW, change(HIX)). spec(Builder) -> _assert_subject = erlang:element(3, Builder), {some, Handler} = case _assert_subject of {some, _} -> _assert_subject; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail, module => <<"filespy"/utf8>>, function => <<"spec"/utf8>>, line => 256}) end, _assert_subject@1 = erlang:element(4, Builder), {some, Initializer} = case _assert_subject@1 of {some, _} -> _assert_subject@1; _assert_fail@1 -> erlang:error(#{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail@1, module => <<"filespy"/utf8>>, function => <<"spec"/utf8>>, line => 257}) end, {spec, fun() -> {Oks, Errs} = begin _pipe = erlang:element(2, Builder), _pipe@2 = gleam@list:map( _pipe, fun(Dir) -> Atom = erlang:binary_to_atom( <<"fs_watcher"/utf8, Dir/binary>> ), _pipe@1 = fs:start_link(Atom, Dir), gleam@result:map(_pipe@1, fun(Pid) -> {Pid, Atom} end) end ), gleam@result:partition(_pipe@2) end, case Errs of [] -> _pipe@3 = Oks, gleam@list:each( _pipe@3, fun(Res) -> {_, Atom@1} = Res, fs:subscribe(Atom@1) end ), {ready, Initializer(), selector()}; Errs@1 -> gleam@list:each( Oks, fun(Res@1) -> {Pid@1, _} = Res@1, gleam@erlang@process:kill(Pid@1) end ), {failed, <<"Failed to start watcher: "/utf8, (gleam@string:inspect(Errs@1))/binary>>} end end, 5000, Handler}. -spec start( builder(any(), has_directories(), has_handler(), has_initial_state(), HJH) ) -> {ok, gleam@erlang@process:subject(change(HJH))} | {error, gleam@otp@actor:start_error()}. start(Builder) -> _pipe = spec(Builder), gleam@otp@actor:start_spec(_pipe).