Current section

Files

Jump to
polly src polly.erl
Raw

src/polly.erl

-module(polly).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([add_dir/2, add_file/2, max_depth/2, interval/2, filter/2, default_filter/2, new/0, ignore_initial_missing/1, stop/1, watch_with/3, watch/2]).
-export_type([event/0, options/1, no_watched_dirs/0, has_watched_dirs/0, watcher/0, vfs/0]).
-type event() :: {created, binary()} |
{changed, binary()} |
{deleted, binary()} |
{error, binary(), simplifile:file_error()}.
-opaque options(GGB) :: {options,
integer(),
list(binary()),
integer(),
fun((simplifile:file_type(), binary()) -> boolean()),
boolean()} |
{gleam_phantom, GGB}.
-type no_watched_dirs() :: any().
-type has_watched_dirs() :: any().
-opaque watcher() :: {watcher, fun(() -> nil)}.
-type vfs() :: {file, binary(), integer()} |
{folder, binary(), integer(), list(vfs())}.
-file("/home/arkan/Projects/private/polly/src/polly.gleam", 96).
-spec add_dir(options(any()), binary()) -> options(has_watched_dirs()).
add_dir(Options, Path) ->
{options, Interval, Paths, Max_depth, Filter, Ignore_initial_missing} = Options,
{options,
Interval,
[Path | Paths],
Max_depth,
Filter,
Ignore_initial_missing}.
-file("/home/arkan/Projects/private/polly/src/polly.gleam", 112).
-spec add_file(options(any()), binary()) -> options(has_watched_dirs()).
add_file(Options, Path) ->
add_dir(Options, Path).
-file("/home/arkan/Projects/private/polly/src/polly.gleam", 127).
-spec max_depth(options(GGM), integer()) -> options(GGM).
max_depth(Options, Max_depth) ->
case (erlang:element(4, Options) < 0) orelse (Max_depth < erlang:element(
4,
Options
)) of
true ->
erlang:setelement(4, Options, Max_depth);
false ->
Options
end.
-file("/home/arkan/Projects/private/polly/src/polly.gleam", 144).
-spec interval(options(GGP), integer()) -> options(GGP).
interval(Options, Interval) ->
case Interval > 0 of
true ->
erlang:setelement(2, Options, Interval);
false ->
Options
end.
-file("/home/arkan/Projects/private/polly/src/polly.gleam", 166).
-spec filter(options(GGS), fun((simplifile:file_type(), binary()) -> boolean())) -> options(GGS).
filter(Options, Filter) ->
erlang:setelement(5, Options, Filter).
-file("/home/arkan/Projects/private/polly/src/polly.gleam", 174).
-spec default_filter(simplifile:file_type(), binary()) -> boolean().
default_filter(_, Path) ->
case filepath:base_name(Path) of
<<"."/utf8>> ->
true;
<<".."/utf8>> ->
true;
Basename ->
not gleam@string:starts_with(Basename, <<"."/utf8>>)
end.
-file("/home/arkan/Projects/private/polly/src/polly.gleam", 79).
-spec new() -> options(no_watched_dirs()).
new() ->
{options, 1000, [], -1, fun default_filter/2, false}.
-file("/home/arkan/Projects/private/polly/src/polly.gleam", 189).
-spec ignore_initial_missing(options(GGV)) -> options(GGV).
ignore_initial_missing(Options) ->
erlang:setelement(6, Options, true).
-file("/home/arkan/Projects/private/polly/src/polly.gleam", 269).
-spec collect_errors(list({binary(), simplifile:file_error()}), event()) -> list({binary(),
simplifile:file_error()}).
collect_errors(Errors, Event) ->
case Event of
{error, Path, Reason} ->
[{Path, Reason} | Errors];
_ ->
Errors
end.
-file("/home/arkan/Projects/private/polly/src/polly.gleam", 283).
-spec stop(watcher()) -> nil.
stop(Watcher) ->
(erlang:element(2, Watcher))().
-file("/home/arkan/Projects/private/polly/src/polly.gleam", 575).
-spec delete(binary(), vfs(), GHZ, fun((GHZ, event()) -> GHZ)) -> GHZ.
delete(Path, Vfs, State, Emit) ->
Full_path = filepath:join(Path, erlang:element(2, Vfs)),
case Vfs of
{file, _, _} ->
Emit(State, {deleted, Full_path});
{folder, _, _, Children} ->
State@2 = gleam@list:fold(
Children,
State,
fun(State@1, Child) ->
delete(Full_path, Child, State@1, Emit)
end
),
Emit(State@2, {deleted, Full_path})
end.
-file("/home/arkan/Projects/private/polly/src/polly.gleam", 594).
-spec get_modkey(simplifile:file_info()) -> integer().
get_modkey(Stat) ->
gleam@int:max(erlang:element(10, Stat), erlang:element(11, Stat)).
-file("/home/arkan/Projects/private/polly/src/polly.gleam", 598).
-spec readdir(binary()) -> {ok, list(binary())} |
{error, simplifile:file_error()}.
readdir(Path) ->
_pipe = simplifile_erl:read_directory(Path),
gleam@result:map(
_pipe,
fun(_capture) ->
gleam@list:sort(_capture, fun gleam@string:compare/2)
end
).
-file("/home/arkan/Projects/private/polly/src/polly.gleam", 461).
-spec diff_children(
fun((simplifile:file_type(), binary()) -> boolean()),
integer(),
binary(),
list(vfs()),
list(binary()),
list(vfs()),
GHX,
fun((GHX, event()) -> GHX)
) -> {list(vfs()), GHX}.
diff_children(
Filter,
Depth,
Path,
Old_children,
New_entries,
New_children,
State,
Emit
) ->
case {Old_children, New_entries} of
{[], []} ->
{lists:reverse(New_children), State};
{[First_old | Rest_old], [First_new | Rest_new]} ->
case gleam@string:compare(erlang:element(2, First_old), First_new) of
eq ->
case diff(Filter, Depth, Path, First_old, State, Emit) of
{{some, New_vfs}, State@1} ->
diff_children(
Filter,
Depth,
Path,
Rest_old,
Rest_new,
[New_vfs | New_children],
State@1,
Emit
);
{none, State@2} ->
diff_children(
Filter,
Depth,
Path,
Rest_old,
Rest_new,
New_children,
State@2,
Emit
)
end;
gt ->
{New_vfs@1, State@3} = create(
Filter,
Depth,
Path,
First_new,
State,
Emit
),
New_children@1 = case New_vfs@1 of
{some, New_vfs@2} ->
[New_vfs@2 | New_children];
none ->
New_children
end,
diff_children(
Filter,
Depth,
Path,
Old_children,
Rest_new,
New_children@1,
State@3,
Emit
);
lt ->
diff_children(
Filter,
Depth,
Path,
Rest_old,
New_entries,
New_children,
delete(Path, First_old, State, Emit),
Emit
)
end;
{[], [First_new@1 | Rest_new@1]} ->
{New_vfs@3, State@4} = create(
Filter,
Depth,
Path,
First_new@1,
State,
Emit
),
New_children@2 = case New_vfs@3 of
{some, New_vfs@4} ->
[New_vfs@4 | New_children];
none ->
New_children
end,
diff_children(
Filter,
Depth,
Path,
Old_children,
Rest_new@1,
New_children@2,
State@4,
Emit
);
{[First_old@1 | Rest_old@1], []} ->
diff_children(
Filter,
Depth,
Path,
Rest_old@1,
New_entries,
New_children,
delete(Path, First_old@1, State, Emit),
Emit
)
end.
-file("/home/arkan/Projects/private/polly/src/polly.gleam", 375).
-spec diff(
fun((simplifile:file_type(), binary()) -> boolean()),
integer(),
binary(),
vfs(),
GHS,
fun((GHS, event()) -> GHS)
) -> {gleam@option:option(vfs()), GHS}.
diff(Filter, Depth, Path, Vfs, State, Emit) ->
Full_path = filepath:join(Path, erlang:element(2, Vfs)),
case simplifile_erl:link_info(Full_path) of
{ok, Stat} ->
Type_ = simplifile:file_info_type(Stat),
case Filter(Type_, Full_path) of
true ->
case {Type_, Vfs} of
{file, {file, Name, Old_key}} ->
New_key = get_modkey(Stat),
case New_key =:= Old_key of
true ->
{{some, Vfs}, State};
false ->
{{some, {file, Name, New_key}},
Emit(State, {changed, Full_path})}
end;
{directory, {folder, _, _, _}} when Depth =:= 0 ->
{{some, Vfs}, State};
{directory, {folder, Name@1, _, Old_children}} when Depth =/= 0 ->
case readdir(Full_path) of
{ok, New_entries} ->
{Children, State@1} = diff_children(
Filter,
Depth - 1,
Full_path,
Old_children,
New_entries,
[],
State,
Emit
),
{{some,
{folder,
Name@1,
get_modkey(Stat),
Children}},
State@1};
{error, enoent} ->
{none, delete(Path, Vfs, State, Emit)};
{error, eacces} ->
{none, delete(Path, Vfs, State, Emit)};
{error, Reason} ->
{{some, Vfs},
Emit(State, {error, Full_path, Reason})}
end;
{_, _} ->
create_stat(
Filter,
Depth,
erlang:element(2, Vfs),
Full_path,
Stat,
delete(Path, Vfs, State, Emit),
Emit
)
end;
false ->
{none, delete(Path, Vfs, State, Emit)}
end;
{error, eacces} ->
{none, delete(Path, Vfs, State, Emit)};
{error, enoent} ->
{none, delete(Path, Vfs, State, Emit)};
{error, Reason@1} ->
{{some, Vfs}, Emit(State, {error, Path, Reason@1})}
end.
-file("/home/arkan/Projects/private/polly/src/polly.gleam", 353).
-spec create_children(
fun((simplifile:file_type(), binary()) -> boolean()),
integer(),
binary(),
list(binary()),
list(vfs()),
GHQ,
fun((GHQ, event()) -> GHQ)
) -> {list(vfs()), GHQ}.
create_children(Filter, Depth, Path, Children, Oks, State, Emit) ->
case Children of
[] ->
{lists:reverse(Oks), State};
[First | Rest] ->
case create(Filter, Depth, Path, First, State, Emit) of
{{some, Vfs}, State@1} ->
create_children(
Filter,
Depth,
Path,
Rest,
[Vfs | Oks],
State@1,
Emit
);
{none, State@2} ->
create_children(
Filter,
Depth,
Path,
Rest,
Oks,
State@2,
Emit
)
end
end.
-file("/home/arkan/Projects/private/polly/src/polly.gleam", 294).
-spec create(
fun((simplifile:file_type(), binary()) -> boolean()),
integer(),
binary(),
binary(),
GHK,
fun((GHK, event()) -> GHK)
) -> {gleam@option:option(vfs()), GHK}.
create(Filter, Depth, Path, Name, State, Emit) ->
Full_path = filepath:join(Path, Name),
case simplifile_erl:link_info(Full_path) of
{ok, Stat} ->
create_stat(Filter, Depth, Name, Full_path, Stat, State, Emit);
{error, enoent} ->
{none, State};
{error, eacces} ->
{none, State};
{error, Reason} ->
{none, Emit(State, {error, Full_path, Reason})}
end.
-file("/home/arkan/Projects/private/polly/src/polly.gleam", 311).
-spec create_stat(
fun((simplifile:file_type(), binary()) -> boolean()),
integer(),
binary(),
binary(),
simplifile:file_info(),
GHM,
fun((GHM, event()) -> GHM)
) -> {gleam@option:option(vfs()), GHM}.
create_stat(Filter, Depth, Name, Full_path, Stat, State, Emit) ->
Type_ = simplifile:file_info_type(Stat),
gleam@bool:guard(
not Filter(Type_, Full_path),
{none, State},
fun() -> case Type_ of
file ->
{{some, {file, Name, get_modkey(Stat)}},
Emit(State, {created, Full_path})};
directory when Depth =:= 0 ->
{{some, {folder, Name, get_modkey(Stat), []}},
Emit(State, {created, Full_path})};
directory when Depth =/= 0 ->
case readdir(Full_path) of
{ok, Entries} ->
Depth@1 = Depth - 1,
State@1 = Emit(State, {created, Full_path}),
{Children, State@2} = create_children(
Filter,
Depth@1,
Full_path,
Entries,
[],
State@1,
Emit
),
{{some, {folder, Name, get_modkey(Stat), Children}},
State@2};
{error, enoent} ->
{none, State};
{error, eacces} ->
{none, State};
{error, Reason} ->
{none, Emit(State, {error, Full_path, Reason})}
end;
_ ->
{none, State}
end end
).
-file("/home/arkan/Projects/private/polly/src/polly.gleam", 219).
-spec watch_with(options(has_watched_dirs()), GHE, fun((GHE, event()) -> GHE)) -> {ok,
watcher()} |
{error, list({binary(), simplifile:file_error()})}.
watch_with(Options, Initial, Emit) ->
{options, Interval, Paths, Max_depth, Filter, Ignore_initial_missing} = Options,
gleam@result:'try'(
(gleam@list:try_map(
Paths,
fun(Path) ->
case create(
Filter,
Max_depth,
Path,
<<""/utf8>>,
[],
fun collect_errors/2
) of
{{some, Vfs}, []} ->
{ok, {Path, {some, Vfs}}};
{none, []} ->
case Ignore_initial_missing of
false ->
{error, [{Path, enoent}]};
true ->
{ok, {Path, none}}
end;
{_, Errors} ->
{error, Errors}
end
end
)),
fun(Roots) ->
State = {Roots, Initial},
Stop = polly_ffi:repeatedly(
Interval,
State,
fun(State@1) ->
{Roots@1, State@2} = State@1,
gleam@list:fold(
Roots@1,
{[], State@2},
fun(_use0, _use1) ->
{Roots@2, State@3} = _use0,
{Path@1, Vfs@1} = _use1,
case Vfs@1 of
{some, Vfs@2} ->
{New_vfs, State@4} = diff(
Filter,
Max_depth,
Path@1,
Vfs@2,
State@3,
Emit
),
{[{Path@1, New_vfs} | Roots@2], State@4};
none ->
{New_vfs@1, State@5} = create(
Filter,
Max_depth,
Path@1,
<<""/utf8>>,
State@3,
Emit
),
{[{Path@1, New_vfs@1} | Roots@2], State@5}
end
end
)
end
),
{ok, {watcher, Stop}}
end
).
-file("/home/arkan/Projects/private/polly/src/polly.gleam", 207).
-spec watch(options(has_watched_dirs()), fun((event()) -> any())) -> {ok,
watcher()} |
{error, list({binary(), simplifile:file_error()})}.
watch(Options, Emit) ->
watch_with(
Options,
nil,
fun(_, Event) ->
Emit(Event),
nil
end
).