Current section

Files

Jump to
ftpasta src ftpasta.erl
Raw

src/ftpasta.erl

-module(ftpasta).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/ftpasta.gleam").
-export([describe_error/1, new/1, connect/1, auth/3, port/2, encryption/2, verification/2, append_bytes/3, change_working_dir/2, close/1, create_dir/2, delete_dir/2, delete_file/2, download_bytes/2, get_working_dir/1, rename/3, upload_bytes/3, list/2, delete_recursive/2, append_file/3, download_file/3, download_recursive/3, upload_file/3, upload_recursive/3]).
-export_type([connection/0, listing/0, config/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 connection() :: {connection, gleam@erlang@process:pid_()}.
-type listing() :: {directory, binary()} |
{file, binary(), integer()} |
{link, binary(), binary()}.
-type config() :: {config,
binary(),
integer(),
ftpasta@tls_config:t_l_s_config(),
boolean(),
binary(),
binary()}.
-file("src/ftpasta.gleam", 29).
?DOC(" Convert an FTPError into a descriptive string.\n").
-spec describe_error(ftpasta@ftp_error:f_t_p_error()) -> binary().
describe_error(Error) ->
case Error of
dir_parse_error ->
<<"Parsing directory contents failed."/utf8>>;
{local_file_error, E} ->
<<"Local file error: "/utf8, (simplifile:describe_error(E))/binary>>;
_ ->
ftpasta_ffi:formaterror(Error)
end.
-file("src/ftpasta.gleam", 38).
?DOC(" Compare two remote files by name.\n").
-spec compare_listings(listing(), listing()) -> gleam@order:order().
compare_listings(A, B) ->
gleam@string:compare(erlang:element(2, A), erlang:element(2, B)).
-file("src/ftpasta.gleam", 57).
?DOC(" Start building an FTP connection.\n").
-spec new(binary()) -> config().
new(Host) ->
{config, Host, 21, plaintext, true, <<"anonymous"/utf8>>, <<""/utf8>>}.
-file("src/ftpasta.gleam", 69).
?DOC(" Connect to the server.\n").
-spec connect(config()) -> {ok, connection()} |
{error, ftpasta@ftp_error:f_t_p_error()}.
connect(Config) ->
gleam@result:'try'(
ftpasta_ffi:open(
erlang:element(2, Config),
erlang:element(3, Config),
erlang:element(4, Config),
erlang:element(5, Config)
),
fun(Pid) ->
gleam@result:map(
ftpasta_ffi:user(
Pid,
erlang:element(6, Config),
erlang:element(7, Config)
),
fun(_) -> {connection, Pid} end
)
end
).
-file("src/ftpasta.gleam", 81).
?DOC(" Provide a username and password.\n").
-spec auth(config(), binary(), binary()) -> config().
auth(Config, Username, Password) ->
{config,
erlang:element(2, Config),
erlang:element(3, Config),
erlang:element(4, Config),
erlang:element(5, Config),
Username,
Password}.
-file("src/ftpasta.gleam", 86).
?DOC(" Use a non-default port.\n").
-spec port(config(), integer()) -> config().
port(Config, Port) ->
{config,
erlang:element(2, Config),
Port,
erlang:element(4, Config),
erlang:element(5, Config),
erlang:element(6, Config),
erlang:element(7, Config)}.
-file("src/ftpasta.gleam", 91).
?DOC(" Use encryption.\n").
-spec encryption(config(), ftpasta@tls_config:t_l_s_config()) -> config().
encryption(Config, Tls) ->
{config,
erlang:element(2, Config),
erlang:element(3, Config),
Tls,
erlang:element(5, Config),
erlang:element(6, Config),
erlang:element(7, Config)}.
-file("src/ftpasta.gleam", 96).
?DOC(" How to verify certificates.\n").
-spec verification(config(), boolean()) -> config().
verification(Config, Enabled) ->
{config,
erlang:element(2, Config),
erlang:element(3, Config),
erlang:element(4, Config),
Enabled,
erlang:element(6, Config),
erlang:element(7, Config)}.
-file("src/ftpasta.gleam", 103).
?DOC(" Append some data onto a file on the server.\n").
-spec append_bytes(connection(), bitstring(), binary()) -> {ok, nil} |
{error, ftpasta@ftp_error:f_t_p_error()}.
append_bytes(Connection, Data, Remote_file) ->
ftpasta_ffi:append_bin(erlang:element(2, Connection), Data, Remote_file).
-file("src/ftpasta.gleam", 114).
?DOC(" Change directories on the remote server.\n").
-spec change_working_dir(connection(), binary()) -> {ok, nil} |
{error, ftpasta@ftp_error:f_t_p_error()}.
change_working_dir(Connection, Remote_dir) ->
ftpasta_ffi:cd(erlang:element(2, Connection), Remote_dir).
-file("src/ftpasta.gleam", 119).
?DOC(" Close the connection.\n").
-spec close(connection()) -> nil.
close(Connection) ->
ftpasta_ffi:close(erlang:element(2, Connection)).
-file("src/ftpasta.gleam", 124).
?DOC(" Create a new directory on the server.\n").
-spec create_dir(connection(), binary()) -> {ok, nil} |
{error, ftpasta@ftp_error:f_t_p_error()}.
create_dir(Connection, Remote_dir) ->
ftpasta_ffi:mkdir(erlang:element(2, Connection), Remote_dir).
-file("src/ftpasta.gleam", 129).
?DOC(" Remove an empty directory from the server.\n").
-spec delete_dir(connection(), binary()) -> {ok, nil} |
{error, ftpasta@ftp_error:f_t_p_error()}.
delete_dir(Connection, Remote_dir) ->
ftpasta_ffi:rmdir(erlang:element(2, Connection), Remote_dir).
-file("src/ftpasta.gleam", 134).
?DOC(" Remove a file from the server.\n").
-spec delete_file(connection(), binary()) -> {ok, nil} |
{error, ftpasta@ftp_error:f_t_p_error()}.
delete_file(Connection, Remote_file) ->
ftpasta_ffi:delete(erlang:element(2, Connection), Remote_file).
-file("src/ftpasta.gleam", 154).
?DOC(" Fetch a file from the server, and return it as a BitArray.\n").
-spec download_bytes(connection(), binary()) -> {ok, bitstring()} |
{error, ftpasta@ftp_error:f_t_p_error()}.
download_bytes(Connection, Remote_file) ->
ftpasta_ffi:recv_bin(erlang:element(2, Connection), Remote_file).
-file("src/ftpasta.gleam", 183).
?DOC(" Get the current remote path used for the connection.\n").
-spec get_working_dir(connection()) -> {ok, binary()} |
{error, ftpasta@ftp_error:f_t_p_error()}.
get_working_dir(Connection) ->
ftpasta_ffi:pwd(erlang:element(2, Connection)).
-file("src/ftpasta.gleam", 205).
?DOC(" Rename a file or directory on the server.\n").
-spec rename(connection(), binary(), binary()) -> {ok, nil} |
{error, ftpasta@ftp_error:f_t_p_error()}.
rename(Connection, Old_path, New_path) ->
ftpasta_ffi:rename(erlang:element(2, Connection), Old_path, New_path).
-file("src/ftpasta.gleam", 210).
?DOC(" Send a data in memory to the server.\n").
-spec upload_bytes(connection(), bitstring(), binary()) -> {ok, nil} |
{error, ftpasta@ftp_error:f_t_p_error()}.
upload_bytes(Connection, Data, Remote_file) ->
ftpasta_ffi:send_bin(erlang:element(2, Connection), Data, Remote_file).
-file("src/ftpasta.gleam", 243).
?DOC(
" Parse a line from the output of \"ls\".\n"
" This is obviously gross, but FTP lacks better options without extensions.\n"
).
-spec parse_ls_line(binary()) -> {ok, listing()} |
{error, ftpasta@ftp_error:f_t_p_error()}.
parse_ls_line(Text) ->
Whitespace@1 = case gleam@regexp:from_string(<<" +"/utf8>>) of
{ok, Whitespace} -> Whitespace;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"ftpasta"/utf8>>,
function => <<"parse_ls_line"/utf8>>,
line => 244,
value => _assert_fail,
start => 6886,
'end' => 6938,
pattern_start => 6897,
pattern_end => 6911})
end,
Filename@1 = case gleam@regexp:from_string(<<"(?:\\S+ +){8}(.+)"/utf8>>) of
{ok, Filename} -> Filename;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"ftpasta"/utf8>>,
function => <<"parse_ls_line"/utf8>>,
line => 245,
value => _assert_fail@1,
start => 6941,
'end' => 7006,
pattern_start => 6952,
pattern_end => 6964})
end,
gleam@result:'try'(case gleam@regexp:scan(Filename@1, Text) of
[{match, _, [{some, X}]}] ->
{ok, X};
_ ->
{error, dir_parse_error}
end, fun(Name) -> case gleam@regexp:split(Whitespace@1, Text) of
[<<"d"/utf8, _/binary>> | _] ->
{ok, {directory, Name}};
[<<"-"/utf8, _/binary>>, _, _, _, Size_text | _] ->
case gleam_stdlib:parse_int(Size_text) of
{ok, Size} ->
{ok, {file, Name, Size}};
{error, _} ->
{error, dir_parse_error}
end;
[<<"l"/utf8, _/binary>> | _] ->
case gleam@string:split_once(Name, <<" -> "/utf8>>) of
{ok, {Name@1, Target}} ->
{ok, {link, Name@1, Target}};
{error, _} ->
{error, dir_parse_error}
end;
_ ->
{error, dir_parse_error}
end end).
-file("src/ftpasta.gleam", 188).
?DOC(" List all files, symlinks, and subdirectories in a directory.\n").
-spec list(connection(), binary()) -> {ok, list(listing())} |
{error, ftpasta@ftp_error:f_t_p_error()}.
list(Connection, Remote_dir) ->
Re@1 = case gleam@regexp:from_string(<<"[\\r\\n]+"/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 => <<"ftpasta"/utf8>>,
function => <<"list"/utf8>>,
line => 192,
value => _assert_fail,
start => 5114,
'end' => 5165,
pattern_start => 5125,
pattern_end => 5131})
end,
Arg = case Remote_dir of
<<""/utf8>> ->
<<"-Al"/utf8>>;
Dir ->
<<"-Al "/utf8, Dir/binary>>
end,
gleam@result:'try'(
ftpasta_ffi:ls(erlang:element(2, Connection), Arg),
fun(Text) -> _pipe = gleam@regexp:split(Re@1, Text),
_pipe@1 = gleam@list:filter(
_pipe,
fun(S) -> not gleam@string:is_empty(S) end
),
_pipe@2 = gleam@list:try_map(_pipe@1, fun parse_ls_line/1),
gleam@result:map(
_pipe@2,
fun(_capture) ->
gleam@list:sort(_capture, fun compare_listings/2)
end
) end
).
-file("src/ftpasta.gleam", 139).
?DOC(" Remove a directory and all contents from the server.\n").
-spec delete_recursive(connection(), binary()) -> {ok, nil} |
{error, ftpasta@ftp_error:f_t_p_error()}.
delete_recursive(Connection, Remote_dir) ->
gleam@result:'try'(
list(Connection, Remote_dir),
fun(Items) ->
gleam@result:'try'(
gleam@list:try_map(
Items,
fun(Item) ->
Path = <<<<Remote_dir/binary, "/"/utf8>>/binary,
(erlang:element(2, Item))/binary>>,
case Item of
{file, _, _} ->
delete_file(Connection, Path);
{link, _, _} ->
delete_file(Connection, Path);
{directory, _} ->
delete_recursive(Connection, Path)
end
end
),
fun(_) -> delete_dir(Connection, Remote_dir) end
)
end
).
-file("src/ftpasta.gleam", 274).
?DOC(" Update the local path stored in the FTP library.\n").
-spec reset_local_dir(connection()) -> {ok, nil} |
{error, ftpasta@ftp_error:f_t_p_error()}.
reset_local_dir(Connection) ->
case simplifile:current_directory() of
{ok, Path} ->
ftpasta_ffi:lcd(erlang:element(2, Connection), Path);
{error, E} ->
{error, {local_file_error, E}}
end.
-file("src/ftpasta.gleam", 108).
?DOC(" Append a file to another file on the server.\n").
-spec append_file(connection(), binary(), binary()) -> {ok, nil} |
{error, ftpasta@ftp_error:f_t_p_error()}.
append_file(Connection, Local_file, Remote_file) ->
gleam@result:'try'(
reset_local_dir(Connection),
fun(_) ->
ftpasta_ffi:append(
erlang:element(2, Connection),
Local_file,
Remote_file
)
end
).
-file("src/ftpasta.gleam", 159).
?DOC(" Fetch a file from the server.\n").
-spec download_file(connection(), binary(), binary()) -> {ok, nil} |
{error, ftpasta@ftp_error:f_t_p_error()}.
download_file(Connection, Remote_file, Local_file) ->
gleam@result:'try'(
reset_local_dir(Connection),
fun(_) ->
ftpasta_ffi:recv(
erlang:element(2, Connection),
Remote_file,
Local_file
)
end
).
-file("src/ftpasta.gleam", 165).
?DOC(" Fetch a directory and all contents recursively from the server.\n").
-spec download_recursive(connection(), binary(), binary()) -> {ok, nil} |
{error, ftpasta@ftp_error:f_t_p_error()}.
download_recursive(Connection, Remote_dir, Local_dir) ->
gleam@result:'try'(
begin
_pipe = simplifile_erl:create_directory(Local_dir),
gleam@result:map_error(
_pipe,
fun(Field@0) -> {local_file_error, Field@0} end
)
end,
fun(_) ->
gleam@result:'try'(
list(Connection, Remote_dir),
fun(Items) ->
_pipe@1 = gleam@list:try_map(
Items,
fun(Item) ->
Local_path = filepath:join(
Local_dir,
erlang:element(2, Item)
),
Remote_path = <<<<Remote_dir/binary, "/"/utf8>>/binary,
(erlang:element(2, Item))/binary>>,
case Item of
{file, _, _} ->
download_file(
Connection,
Remote_path,
Local_path
);
{link, _, _} ->
download_file(
Connection,
Remote_path,
Local_path
);
{directory, _} ->
download_recursive(
Connection,
Remote_path,
Local_path
)
end
end
),
gleam@result:replace(_pipe@1, nil)
end
)
end
).
-file("src/ftpasta.gleam", 215).
?DOC(" Send a file to the server.\n").
-spec upload_file(connection(), binary(), binary()) -> {ok, nil} |
{error, ftpasta@ftp_error:f_t_p_error()}.
upload_file(Connection, Local_file, Remote_file) ->
gleam@result:'try'(
reset_local_dir(Connection),
fun(_) ->
ftpasta_ffi:send(
erlang:element(2, Connection),
Local_file,
Remote_file
)
end
).
-file("src/ftpasta.gleam", 221).
?DOC(" Send a directory and all contents to the server.\n").
-spec upload_recursive(connection(), binary(), binary()) -> {ok, nil} |
{error, ftpasta@ftp_error:f_t_p_error()}.
upload_recursive(Connection, Local_dir, Remote_dir) ->
gleam@result:'try'(
create_dir(Connection, Remote_dir),
fun(_) ->
gleam@result:'try'(
begin
_pipe = simplifile_erl:read_directory(Local_dir),
gleam@result:map_error(
_pipe,
fun(Field@0) -> {local_file_error, Field@0} end
)
end,
fun(Items) ->
_pipe@1 = gleam@list:try_map(
Items,
fun(Name) ->
Local_path = filepath:join(Local_dir, Name),
Remote_path = <<<<Remote_dir/binary, "/"/utf8>>/binary,
Name/binary>>,
case simplifile_erl:is_file(Local_path) of
{ok, true} ->
upload_file(
Connection,
Local_path,
Remote_path
);
{ok, false} ->
upload_recursive(
Connection,
Local_path,
Remote_path
);
{error, E} ->
{error, {local_file_error, E}}
end
end
),
gleam@result:replace(_pipe@1, nil)
end
)
end
).