Current section
Files
Jump to
Current section
Files
src/gftp@list.erl
-module(gftp@list).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gftp/list.gleam").
-export([describe_error/1, parse_mlsd/1, parse_mlst/1, parse_list/1]).
-export_type([parse_error/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.
?MODULEDOC(
" Parsers for FTP directory listing formats: `LIST` (POSIX and DOS), `MLSD`, and `MLST`.\n"
"\n"
" ## LIST parsing\n"
"\n"
" The `LIST` output format is not standardized and depends on the FTP server.\n"
" This parser supports both POSIX and DOS formats, trying POSIX first.\n"
" If you find a format that doesn't parse correctly, please report an issue\n"
" at <https://github.com/veeso/gftp>.\n"
"\n"
" ```gleam\n"
" import gftp/list\n"
" import gftp/list/file\n"
"\n"
" let line = \"-rw-r--r-- 1 user group 1234 Nov 5 13:46 example.txt\"\n"
" let assert Ok(f) = list.parse_list(line)\n"
" let name = file.name(f) // \"example.txt\"\n"
" let size = file.size(f) // 1234\n"
" ```\n"
"\n"
" ## MLSD/MLST parsing (RFC 3659)\n"
"\n"
" Machine-readable listing formats provide structured, standardized output:\n"
"\n"
" ```gleam\n"
" import gftp/list\n"
" import gftp/list/file\n"
"\n"
" let line = \"type=file;size=1234;modify=20200105134600; example.txt\"\n"
" let assert Ok(f) = list.parse_mlsd(line)\n"
" let name = file.name(f) // \"example.txt\"\n"
" ```\n"
"\n"
" ## Usage with gftp\n"
"\n"
" ```gleam\n"
" import gftp\n"
" import gftp/list\n"
" import gleam/list as gleam_list\n"
" import gleam/option.{None}\n"
"\n"
" let assert Ok(lines) = gftp.list(client, None)\n"
" let assert Ok(files) = gleam_list.try_map(lines, list.parse_list)\n"
" ```\n"
).
-type parse_error() :: syntax_error | invalid_date | bad_size.
-file("src/gftp/list.gleam", 67).
?DOC(" Collapse multiple consecutive whitespace characters into a single space.\n").
-spec normalize_whitespace(binary()) -> binary().
normalize_whitespace(Input) ->
Re@1 = case gleam@regexp:from_string(<<"\\s+"/utf8>>) of
{ok, Re} -> Re;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"gftp/list"/utf8>>,
function => <<"normalize_whitespace"/utf8>>,
line => 68,
value => _assert_fail,
start => 2230,
'end' => 2276,
pattern_start => 2241,
pattern_end => 2247})
end,
gleam_regexp_ffi:replace(Re@1, Input, <<" "/utf8>>).
-file("src/gftp/list.gleam", 84).
?DOC(" Describe the given `ParseError` in human-readable form.\n").
-spec describe_error(parse_error()) -> binary().
describe_error(Error) ->
case Error of
syntax_error ->
<<"the line does not conform to expected format."/utf8>>;
invalid_date ->
<<"the date field could not be parsed."/utf8>>;
bad_size ->
<<"the size field is not a valid unsigned integer."/utf8>>
end.
-file("src/gftp/list.gleam", 138).
?DOC(" Parse the permissions string from a `MLSD` or `MLST` line, which is typically a three-digit octal number representing the permissions for the owner, group, and others in a POSIX file system. Each digit can be from 0 to 7, where the bits represent read (4), write (2), and execute (1) permissions.\n").
-spec parse_mlsd_mlst_permissions(binary()) -> {ok,
gftp@list@permission:file_permissions()} |
{error, parse_error()}.
parse_mlsd_mlst_permissions(Pex_str) ->
Tokens = begin
_pipe = Pex_str,
_pipe@1 = gleam@string:split(_pipe, <<""/utf8>>),
gleam@list:map(_pipe@1, fun(Token) -> _pipe@2 = Token,
_pipe@3 = gleam@int:base_parse(_pipe@2, 8),
gleam@result:unwrap(_pipe@3, 0) end)
end,
case Tokens of
[User, Group, Other] ->
{ok,
{file_permissions,
gftp@list@permission:from_int(User),
gftp@list@permission:from_int(Group),
gftp@list@permission:from_int(Other)}};
[_, User, Group, Other] ->
{ok,
{file_permissions,
gftp@list@permission:from_int(User),
gftp@list@permission:from_int(Group),
gftp@list@permission:from_int(Other)}};
_ ->
{error, syntax_error}
end.
-file("src/gftp/list.gleam", 230).
?DOC(" Parse the file type token from a POSIX LIST output line, which is typically the first character of the line and can be `-` for regular files, `d` for directories, and `l` for symbolic links (although we can't retrieve the target of the symlink in this case).\n").
-spec parse_list_file_type(binary()) -> {ok, gftp@list@file_type:file_type()} |
{error, parse_error()}.
parse_list_file_type(Token) ->
case Token of
<<"-"/utf8>> ->
{ok, file};
<<"d"/utf8>> ->
{ok, directory};
<<"l"/utf8>> ->
{ok, {symlink, <<""/utf8>>}};
_ ->
{error, syntax_error}
end.
-file("src/gftp/list.gleam", 241).
?DOC(" Parse a group permissions set made of three characters (e.g. `rwx`, `r-x`, `rw-`, etc.) from a POSIX LIST output line, where the characters represent read (`r`), write (`w`), and execute (`x`) permissions, and `-` represents no permission. Additionally, `s` and `t` can be used to indicate setuid/setgid and sticky bits, respectively.\n").
-spec parse_list_permissions_in_range(integer(), binary()) -> gftp@list@permission:posix_permission().
parse_list_permissions_in_range(Start, Token) ->
_pipe = Token,
_pipe@1 = gleam@string:slice(_pipe, Start, 3),
_pipe@2 = gleam@string:split(_pipe@1, <<""/utf8>>),
_pipe@3 = gleam@list:fold(_pipe@2, 0, fun(Acc, Char) -> case Char of
<<"r"/utf8>> ->
Acc + 4;
<<"w"/utf8>> ->
Acc + 2;
<<"x"/utf8>> ->
Acc + 1;
<<"s"/utf8>> ->
Acc + 1;
<<"t"/utf8>> ->
Acc + 1;
<<"-"/utf8>> ->
Acc;
_ ->
Acc
end end),
gftp@list@permission:from_int(_pipe@3).
-file("src/gftp/list.gleam", 265).
?DOC(
" Parse the permissions string from a POSIX LIST output line, \n"
" which is typically a 10-character string where the first character \n"
" represents the file type and the next 9 characters represent the \n"
" permissions for the owner, group, and others in a POSIX file system. \n"
" The permissions are represented as `r` for read, `w` for write, `x` for execute, and `-` for no permission. \n"
" Additionally, `s` and `t` can be used to indicate setuid/setgid and sticky bits, respectively.\n"
).
-spec parse_list_permissions(binary()) -> gftp@list@permission:file_permissions().
parse_list_permissions(Token) ->
{file_permissions,
parse_list_permissions_in_range(0, Token),
parse_list_permissions_in_range(3, Token),
parse_list_permissions_in_range(6, Token)}.
-file("src/gftp/list.gleam", 328).
?DOC(
" Parse the file name and symlink target (if applicable) from a POSIX LIST output line, which is typically the last token of the line.\n"
" If the file type is a symbolic link, the token may contain the file name followed by `->` and the target of the symlink (e.g. `symlink -> target`).\n"
).
-spec parse_list_name_and_link(gftp@list@file_type:file_type(), binary()) -> {ok,
{binary(), gleam@option:option(binary())}} |
{error, parse_error()}.
parse_list_name_and_link(File_type, Token) ->
Get_name_and_link = fun(Parts) -> case Parts of
[Name, Target] ->
{ok, {Name, {some, Target}}};
[Name@1] ->
{ok, {Name@1, none}};
_ ->
{error, syntax_error}
end end,
case File_type of
{symlink, _} ->
_pipe = Token,
_pipe@1 = gleam@string:split(_pipe, <<" -> "/utf8>>),
Get_name_and_link(_pipe@1);
_ ->
{ok, {Token, none}}
end.
-file("src/gftp/list.gleam", 351).
?DOC(
" Resolve the file type for a POSIX LIST entry, filling in the symlink target\n"
" when both the type indicator is `l` and the name contained ` -> target`.\n"
).
-spec resolve_posix_file_type(
gftp@list@file_type:file_type(),
gleam@option:option(binary())
) -> gftp@list@file_type:file_type().
resolve_posix_file_type(Ft, Symlink_path) ->
case {Ft, Symlink_path} of
{{symlink, _}, {some, Target}} ->
{symlink, Target};
{{symlink, _}, none} ->
file;
{_, _} ->
Ft
end.
-file("src/gftp/list.gleam", 363).
?DOC(" Optionally set a field on a File when the value is Some.\n").
-spec maybe_set(
gftp@list@file:file(),
gleam@option:option(MDF),
fun((gftp@list@file:file(), MDF) -> gftp@list@file:file())
) -> gftp@list@file:file().
maybe_set(F, Value, Setter) ->
case Value of
{some, V} ->
Setter(F, V);
none ->
F
end.
-file("src/gftp/list.gleam", 371).
?DOC(" Try to parse a string as an Int, returning None on failure.\n").
-spec try_parse_int(binary()) -> gleam@option:option(integer()).
try_parse_int(S) ->
_pipe = S,
_pipe@1 = gleam@string:trim(_pipe),
_pipe@2 = gleam_stdlib:parse_int(_pipe@1),
gleam@option:from_result(_pipe@2).
-file("src/gftp/list.gleam", 479).
?DOC(" Try to a string as a datetime in the provided format, returning a Timestamp on success or a ParseError on failure.\n").
-spec parse_datetime_with_format(binary(), binary()) -> {ok,
gleam@time@timestamp:timestamp()} |
{error, parse_error()}.
parse_datetime_with_format(S, Format) ->
Fmt = {custom_naive, Format},
_pipe = S,
_pipe@1 = gleam@string:trim(_pipe),
_pipe@2 = normalize_whitespace(_pipe@1),
_pipe@3 = tempo@naive_datetime:parse(_pipe@2, Fmt),
_pipe@4 = gleam@result:map(_pipe@3, fun tempo@naive_datetime:as_utc/1),
_pipe@5 = gleam@result:map(_pipe@4, fun tempo@datetime:to_timestamp/1),
gleam@result:map_error(_pipe@5, fun(_) -> invalid_date end).
-file("src/gftp/list.gleam", 133).
?DOC(" Parse MLSD and MLST date formats, which is in the form of `%Y%m%d%H%M%S`\n").
-spec parse_mlsd_mlst_time(binary()) -> {ok, gleam@time@timestamp:timestamp()} |
{error, parse_error()}.
parse_mlsd_mlst_time(Date_str) ->
parse_datetime_with_format(Date_str, <<"YYYYMMDDHHmmss"/utf8>>).
-file("src/gftp/list.gleam", 159).
?DOC(" Parse a single token in a `key=value` pair from a `MLSD` or `MLST` line, updating the given `File` data structure with the extracted metadata on success, or returning a `ParseError` on failure.\n").
-spec parse_mlsd_mlst_token(binary(), binary(), gftp@list@file:file()) -> {ok,
gftp@list@file:file()} |
{error, parse_error()}.
parse_mlsd_mlst_token(Key, Value, File) ->
case string:lowercase(Key) of
<<"type"/utf8>> ->
case string:lowercase(Value) of
<<"file"/utf8>> ->
{ok, gftp@list@file:with_file_type(File, file)};
<<"link"/utf8>> ->
{ok, gftp@list@file:with_file_type(File, file)};
<<"dir"/utf8>> ->
{ok, gftp@list@file:with_file_type(File, directory)};
<<"cdir"/utf8>> ->
{ok, gftp@list@file:with_file_type(File, directory)};
<<"pdir"/utf8>> ->
{ok, gftp@list@file:with_file_type(File, directory)};
_ ->
{error, syntax_error}
end;
<<"size"/utf8>> ->
_pipe = Value,
_pipe@1 = gleam_stdlib:parse_int(_pipe),
_pipe@2 = gleam@result:map(
_pipe@1,
fun(Size) -> gftp@list@file:with_size(File, Size) end
),
gleam@result:map_error(_pipe@2, fun(_) -> bad_size end);
<<"modify"/utf8>> ->
_pipe@3 = Value,
_pipe@4 = parse_mlsd_mlst_time(_pipe@3),
gleam@result:map(
_pipe@4,
fun(Modified) ->
gftp@list@file:with_modified(File, Modified)
end
);
<<"unix.uid"/utf8>> ->
_pipe@5 = Value,
_pipe@6 = gleam_stdlib:parse_int(_pipe@5),
_pipe@7 = gleam@result:map(
_pipe@6,
fun(Uid) -> gftp@list@file:with_uid(File, Uid) end
),
gleam@result:map_error(_pipe@7, fun(_) -> syntax_error end);
<<"unix.gid"/utf8>> ->
_pipe@8 = Value,
_pipe@9 = gleam_stdlib:parse_int(_pipe@8),
_pipe@10 = gleam@result:map(
_pipe@9,
fun(Uid@1) -> gftp@list@file:with_gid(File, Uid@1) end
),
gleam@result:map_error(_pipe@10, fun(_) -> syntax_error end);
<<"unix.mode"/utf8>> ->
_pipe@11 = Value,
_pipe@12 = parse_mlsd_mlst_permissions(_pipe@11),
_pipe@13 = gleam@result:map(
_pipe@12,
fun(Permissions) ->
gftp@list@file:with_permissions(File, Permissions)
end
),
gleam@result:map_error(_pipe@13, fun(_) -> syntax_error end);
_ ->
{ok, File}
end.
-file("src/gftp/list.gleam", 199).
?DOC(" Parse MLSD and MLST tokens, split by `;`\n").
-spec parse_mlsd_mlst_tokens(list(binary()), gftp@list@file:file()) -> {ok,
gftp@list@file:file()} |
{error, parse_error()}.
parse_mlsd_mlst_tokens(Tokens, File) ->
case Tokens of
[] ->
{ok, File};
[Filename] ->
parse_mlsd_mlst_tokens(
[],
gftp@list@file:with_name(File, gleam@string:trim(Filename))
);
[Token | Rest] ->
case gleam@string:split(Token, <<"="/utf8>>) of
[Key, Value] ->
case parse_mlsd_mlst_token(Key, Value, File) of
{ok, Updated_file} ->
parse_mlsd_mlst_tokens(Rest, Updated_file);
{error, Error} ->
{error, Error}
end;
_ ->
{error, syntax_error}
end
end.
-file("src/gftp/list.gleam", 219).
?DOC(
" Parse any line from a `MLSD` or `MLST` command output,\n"
" returning a `File` data structure with the extracted metadata on success, or a `ParseError` on failure.\n"
).
-spec parse_mlsd_mlst_line(binary()) -> {ok, gftp@list@file:file()} |
{error, parse_error()}.
parse_mlsd_mlst_line(Line) ->
case gleam@string:trim(Line) of
<<""/utf8>> ->
{error, syntax_error};
Trimmed ->
_pipe = Trimmed,
_pipe@1 = gleam@string:split(_pipe, <<";"/utf8>>),
parse_mlsd_mlst_tokens(_pipe@1, gftp@list@file:empty())
end.
-file("src/gftp/list.gleam", 98).
?DOC(
" Parse a line from `MLSD` command output into a `File`.\n"
"\n"
" ```gleam\n"
" let line = \"type=file;size=1024;modify=20200105134600; readme.txt\"\n"
" let assert Ok(f) = list.parse_mlsd(line)\n"
" ```\n"
).
-spec parse_mlsd(binary()) -> {ok, gftp@list@file:file()} |
{error, parse_error()}.
parse_mlsd(Line) ->
parse_mlsd_mlst_line(Line).
-file("src/gftp/list.gleam", 108).
?DOC(
" Parse a line from `MLST` command output into a `File`.\n"
"\n"
" ```gleam\n"
" let line = \"type=file;size=2048;modify=20210315120000; document.pdf\"\n"
" let assert Ok(f) = list.parse_mlst(line)\n"
" ```\n"
).
-spec parse_mlst(binary()) -> {ok, gftp@list@file:file()} |
{error, parse_error()}.
parse_mlst(Line) ->
parse_mlsd_mlst_line(Line).
-file("src/gftp/list.gleam", 309).
?DOC(
" If `parsed` is more than ~6 months in the future relative to `now`,\n"
" re-parse using the previous year.\n"
).
-spec adjust_year_if_future(
gleam@time@timestamp:timestamp(),
gleam@time@timestamp:timestamp(),
integer(),
binary()
) -> {ok, gleam@time@timestamp:timestamp()} | {error, parse_error()}.
adjust_year_if_future(Parsed, Now, Current_year, Normalized) ->
Six_months_in_seconds = (180 * 24) * 3600,
Diff = gleam@time@timestamp:difference(Parsed, Now),
case gleam@time@duration:compare(
Diff,
gleam@time@duration:seconds(Six_months_in_seconds)
) of
gt ->
_pipe = (<<<<(erlang:integer_to_binary(Current_year - 1))/binary,
" "/utf8>>/binary,
Normalized/binary>>),
parse_datetime_with_format(_pipe, <<"YYYY MMM D HH:mm"/utf8>>);
_ ->
{ok, Parsed}
end.
-file("src/gftp/list.gleam", 275).
?DOC(
" Parse the last modification time from a POSIX LIST output line, which can be in one of two formats: `MMM D YYYY` (e.g. `Nov 5 2020`)\n"
" or `MMM D HH:mm` (e.g. `Nov 5 13:46`). The parser will first try to parse the date using the year format, and if that fails, it will try to parse it using the time format.\n"
).
-spec parse_list_lstime(binary()) -> {ok, gleam@time@timestamp:timestamp()} |
{error, parse_error()}.
parse_list_lstime(Token) ->
Normalized = begin
_pipe = Token,
_pipe@1 = gleam@string:trim(_pipe),
normalize_whitespace(_pipe@1)
end,
Year_result = begin
_pipe@2 = (<<Normalized/binary, " 00:00"/utf8>>),
parse_datetime_with_format(_pipe@2, <<"MMM D YYYY HH:mm"/utf8>>)
end,
Now = gleam@time@timestamp:system_time(),
Current_year = begin
_pipe@3 = Now,
_pipe@4 = gleam@time@timestamp:to_calendar(
_pipe@3,
gleam@time@duration:seconds(0)
),
(fun(Res) -> erlang:element(2, (erlang:element(1, Res))) end)(_pipe@4)
end,
Time_result = begin
_pipe@5 = (<<<<(erlang:integer_to_binary(Current_year))/binary,
" "/utf8>>/binary,
Normalized/binary>>),
_pipe@6 = parse_datetime_with_format(
_pipe@5,
<<"YYYY MMM D HH:mm"/utf8>>
),
gleam@result:'try'(
_pipe@6,
fun(Parsed) ->
adjust_year_if_future(Parsed, Now, Current_year, Normalized)
end
)
end,
_pipe@7 = Year_result,
_pipe@8 = gleam@result:'or'(_pipe@7, Time_result),
gleam@result:map_error(_pipe@8, fun(_) -> invalid_date end).
-file("src/gftp/list.gleam", 383).
?DOC(
" Parse a POSIX LIST output line and if it is valid, return a [`File`] instance, otherwise return a [`ParseError`].\n"
"\n"
" POSIX syntax has the following syntax:\n"
"\n"
" ```text\n"
" {FILE_TYPE}{PERMISSIONS} {LINK_COUNT} {USER} {GROUP} {FILE_SIZE} {MODIFIED_TIME} {FILENAME}\n"
" -rw-r--r-- 1 user group 1234 Nov 5 13:46 example.txt\n"
" ```\n"
).
-spec parse_list_posix(binary(), gleam@regexp:regexp()) -> {ok,
gftp@list@file:file()} |
{error, parse_error()}.
parse_list_posix(Line, Re) ->
case gftp@internal@utils:re_matches(Re, Line) of
{ok,
[{some, Ft_str},
{some, Perm_str},
_,
{some, User},
{some, Group},
{some, Size_str},
{some, Modified_str},
{some, Name_str}]} ->
gleam@result:'try'(
parse_list_file_type(Ft_str),
fun(Ft) ->
gleam@result:'try'(
begin
_pipe = Size_str,
_pipe@1 = gleam@string:trim(_pipe),
_pipe@2 = gleam_stdlib:parse_int(_pipe@1),
gleam@result:replace_error(_pipe@2, bad_size)
end,
fun(Size) ->
gleam@result:'try'(
begin
_pipe@3 = Modified_str,
_pipe@4 = parse_list_lstime(_pipe@3),
gleam@result:replace_error(
_pipe@4,
invalid_date
)
end,
fun(Modified) ->
gleam@result:'try'(
parse_list_name_and_link(Ft, Name_str),
fun(_use0) ->
{Name, Symlink_path} = _use0,
_pipe@5 = gftp@list@file:empty(),
_pipe@6 = gftp@list@file:with_name(
_pipe@5,
Name
),
_pipe@7 = gftp@list@file:with_file_type(
_pipe@6,
resolve_posix_file_type(
Ft,
Symlink_path
)
),
_pipe@8 = gftp@list@file:with_size(
_pipe@7,
Size
),
_pipe@9 = gftp@list@file:with_modified(
_pipe@8,
Modified
),
_pipe@10 = gftp@list@file:with_permissions(
_pipe@9,
parse_list_permissions(Perm_str)
),
_pipe@11 = maybe_set(
_pipe@10,
try_parse_int(User),
fun gftp@list@file:with_uid/2
),
_pipe@12 = maybe_set(
_pipe@11,
try_parse_int(Group),
fun gftp@list@file:with_gid/2
),
{ok, _pipe@12}
end
)
end
)
end
)
end
);
_ ->
{error, syntax_error}
end.
-file("src/gftp/list.gleam", 472).
?DOC(
" Parse a date and time in the format used by DOS LIST output, \n"
" which is typically `MM-DD-YY hh:mmA` (e.g. `10-19-20 03:19PM`), or with a space before the AM/PM (e.g. `10-19-20 03:19 PM`).\n"
).
-spec parse_list_dos_time(binary()) -> {ok, gleam@time@timestamp:timestamp()} |
{error, parse_error()}.
parse_list_dos_time(Token) ->
_pipe = Token,
_pipe@1 = parse_datetime_with_format(_pipe, <<"MM-DD-YY hh:mmA"/utf8>>),
gleam@result:'or'(
_pipe@1,
parse_datetime_with_format(Token, <<"MM-DD-YY hh:mm A"/utf8>>)
).
-file("src/gftp/list.gleam", 435).
?DOC(
" Try to parse a \"LIST\" output command line in DOS format.\n"
" Returns [`ParseError`] if syntax is not DOS compliant.\n"
" DOS syntax has the following syntax:\n"
"\n"
" ```text\n"
" {DATE} {TIME} {<DIR> | SIZE} {FILENAME}\n"
" 10-19-20 03:19PM <DIR> pub\n"
" 04-08-14 03:09PM 403 readme.txt\n"
" ```\n"
).
-spec parse_list_dos(binary(), gleam@regexp:regexp()) -> {ok,
gftp@list@file:file()} |
{error, parse_error()}.
parse_list_dos(Line, Re) ->
case gftp@internal@utils:re_matches(Re, Line) of
{ok, [{some, Modified}, File_type, Size, {some, Name}]} ->
gleam@result:'try'(
begin
_pipe = Modified,
_pipe@1 = parse_list_dos_time(_pipe),
gleam@result:replace_error(_pipe@1, invalid_date)
end,
fun(Modified@1) ->
File_type@1 = case gleam@option:is_some(File_type) of
true ->
directory;
false ->
file
end,
gleam@result:'try'(case Size of
none ->
{ok, 0};
{some, Size_str} ->
_pipe@2 = Size_str,
_pipe@3 = gleam@string:trim(_pipe@2),
_pipe@4 = gleam@string:replace(
_pipe@3,
<<","/utf8>>,
<<""/utf8>>
),
_pipe@5 = gleam_stdlib:parse_int(_pipe@4),
gleam@result:replace_error(_pipe@5, bad_size)
end, fun(Size@1) -> _pipe@6 = gftp@list@file:empty(),
_pipe@7 = gftp@list@file:with_name(_pipe@6, Name),
_pipe@8 = gftp@list@file:with_file_type(
_pipe@7,
File_type@1
),
_pipe@9 = gftp@list@file:with_size(_pipe@8, Size@1),
_pipe@10 = gftp@list@file:with_modified(
_pipe@9,
Modified@1
),
{ok, _pipe@10} end)
end
);
_ ->
{error, syntax_error}
end.
-file("src/gftp/list.gleam", 123).
?DOC(
" Parse a `LIST` output line (POSIX or DOS format) into a `File`.\n"
"\n"
" Tries POSIX format first, then falls back to DOS format.\n"
"\n"
" ```gleam\n"
" // POSIX format\n"
" let assert Ok(f) = list.parse_list(\"-rw-r--r-- 1 user group 1234 Nov 5 13:46 example.txt\")\n"
"\n"
" // DOS format\n"
" let assert Ok(f) = list.parse_list(\"10-19-20 03:19PM 403 readme.txt\")\n"
" ```\n"
).
-spec parse_list(binary()) -> {ok, gftp@list@file:file()} |
{error, parse_error()}.
parse_list(Line) ->
Posix_re@1 = case gleam@regexp:from_string(
<<"^([\\-ld])([\\-rwxsStT]{9})\\s+(\\d+)\\s+([^ ]+)\\s+([^ ]+)\\s+(\\d+)\\s+([^ ]+\\s+\\d{1,2}\\s+(?:\\d{1,2}:\\d{1,2}|\\d{4}))\\s+(.+)$"/utf8>>
) of
{ok, Posix_re} -> Posix_re;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"gftp/list"/utf8>>,
function => <<"parse_list"/utf8>>,
line => 124,
value => _assert_fail,
start => 3932,
'end' => 3994,
pattern_start => 3943,
pattern_end => 3955})
end,
Dos_re@1 = case gleam@regexp:from_string(
<<"^(\\d{2}\\-\\d{2}\\-\\d{2}\\s+\\d{2}:\\d{2}\\s*[AP]M)\\s+(<DIR>)?([\\d,]*)\\s+(.+)$"/utf8>>
) of
{ok, Dos_re} -> Dos_re;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"gftp/list"/utf8>>,
function => <<"parse_list"/utf8>>,
line => 125,
value => _assert_fail@1,
start => 3997,
'end' => 4055,
pattern_start => 4008,
pattern_end => 4018})
end,
_pipe = Line,
_pipe@1 = parse_list_posix(_pipe, Posix_re@1),
gleam@result:'or'(_pipe@1, parse_list_dos(Line, Dos_re@1)).