Current section
Files
Jump to
Current section
Files
src/birdie.erl
-module(birdie).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([main/0, snap/2]).
-export_type([error/0, new/0, accepted/0, snapshot/1, outcome/0, info_line/0, split/0, review_choice/0]).
-type error() :: {cannot_create_snapshots_folder, simplifile:file_error()} |
{cannot_read_accepted_snapshot, simplifile:file_error(), binary()} |
{cannot_read_new_snapshot, simplifile:file_error(), binary()} |
{cannot_save_new_snapshot, simplifile:file_error(), binary(), binary()} |
{cannot_read_snapshots, simplifile:file_error(), binary()} |
{cannot_reject_snapshot, simplifile:file_error(), binary()} |
{cannot_accept_snapshot, simplifile:file_error(), binary()} |
cannot_read_user_input |
{corrupted_snapshot, binary()} |
{cannot_find_project_root, simplifile:file_error()}.
-type new() :: any().
-type accepted() :: any().
-type snapshot(IFS) :: {snapshot, binary(), binary()} | {gleam_phantom, IFS}.
-type outcome() :: {new_snapshot_created, snapshot(new()), binary()} |
{different, snapshot(accepted()), snapshot(new())} |
same.
-type info_line() :: {info_line_with_title, binary(), split(), binary()} |
{info_line_with_no_title, binary(), split()}.
-type split() :: do_not_split | split_words | truncate.
-type review_choice() :: accept_snapshot | reject_snapshot | skip_snapshot.
-spec to_diff_lines(snapshot(accepted()), snapshot(new())) -> list(birdie@internal@diff:diff_line()).
to_diff_lines(Accepted, New) ->
{snapshot, _, Accepted_content} = Accepted,
{snapshot, _, New_content} = New,
birdie@internal@diff:line_by_line(Accepted_content, New_content).
-spec deserialise(binary()) -> {ok, snapshot(any())} | {error, nil}.
deserialise(Raw) ->
gleam@result:'try'(
gleam@string:split_once(Raw, <<"\n"/utf8>>),
fun(_use0) ->
{Open_line, Rest} = _use0,
gleam@bool:guard(
Open_line /= <<"---"/utf8>>,
{error, nil},
fun() ->
gleam@result:'try'(
gleam@string:split_once(Rest, <<"\n"/utf8>>),
fun(_use0@1) ->
{Version_line, Rest@1} = _use0@1,
gleam@result:'try'(case Version_line of
<<"version: "/utf8, Version/binary>> ->
{ok, Version};
_ ->
{error, nil}
end, fun(_) ->
gleam@result:'try'(
gleam@string:split_once(
Rest@1,
<<"\n"/utf8>>
),
fun(_use0@2) ->
{Title_line, Rest@2} = _use0@2,
gleam@result:'try'(
case Title_line of
<<"title: "/utf8, Title/binary>> ->
{ok,
gleam@string:replace(
Title,
<<"\\n"/utf8>>,
<<"\n"/utf8>>
)};
_ ->
{error, nil}
end,
fun(Title@1) ->
gleam@result:'try'(
gleam@string:split_once(
Rest@2,
<<"\n"/utf8>>
),
fun(_use0@3) ->
{Close_line,
Content} = _use0@3,
gleam@bool:guard(
Close_line /= <<"---"/utf8>>,
{error, nil},
fun() ->
{ok,
{snapshot,
Title@1,
Content}}
end
)
end
)
end
)
end
)
end)
end
)
end
)
end
).
-spec read_accepted(binary()) -> {ok, gleam@option:option(snapshot(accepted()))} |
{error, error()}.
read_accepted(Source) ->
case simplifile:read(Source) of
{ok, Content} ->
case deserialise(Content) of
{ok, Snapshot} ->
{ok, {some, Snapshot}};
{error, nil} ->
{error, {corrupted_snapshot, Source}}
end;
{error, enoent} ->
{ok, none};
{error, Reason} ->
{error, {cannot_read_accepted_snapshot, Reason, Source}}
end.
-spec read_new(binary()) -> {ok, snapshot(new())} | {error, error()}.
read_new(Source) ->
case simplifile:read(Source) of
{ok, Content} ->
gleam@result:replace_error(
deserialise(Content),
{corrupted_snapshot, Source}
);
{error, Reason} ->
{error, {cannot_read_new_snapshot, Reason, Source}}
end.
-spec list_new_snapshots(binary()) -> {ok, list(binary())} | {error, error()}.
list_new_snapshots(Folder) ->
case simplifile:read_directory(Folder) of
{error, Reason} ->
{error, {cannot_read_snapshots, Reason, Folder}};
{ok, Files} ->
{ok,
(gleam@list:filter_map(
Files,
fun(File) -> case filepath:extension(File) of
{ok, <<"new"/utf8>>} ->
{ok, filepath:join(Folder, File)};
_ ->
{error, nil}
end end
))}
end.
-spec find_project_root(binary()) -> {ok, binary()} |
{error, simplifile:file_error()}.
find_project_root(Path) ->
Manifest = filepath:join(Path, <<"gleam.toml"/utf8>>),
case simplifile:verify_is_file(Manifest) of
{ok, true} ->
{ok, Path};
{ok, false} ->
find_project_root(filepath:join(Path, <<".."/utf8>>));
{error, Reason} ->
{error, Reason}
end.
-spec reject_snapshot(binary()) -> {ok, nil} | {error, error()}.
reject_snapshot(New_snapshot_path) ->
_pipe = simplifile:delete(New_snapshot_path),
gleam@result:map_error(
_pipe,
fun(_capture) ->
{cannot_reject_snapshot, _capture, New_snapshot_path}
end
).
-spec file_name(binary()) -> binary().
file_name(Title) ->
_pipe = gleam@string:replace(Title, <<"/"/utf8>>, <<" "/utf8>>),
_pipe@1 = gleam@string:replace(_pipe, <<"\\"/utf8>>, <<" "/utf8>>),
_pipe@2 = gleam@string:replace(_pipe@1, <<"\n"/utf8>>, <<" "/utf8>>),
_pipe@3 = gleam@string:replace(_pipe@2, <<"\t"/utf8>>, <<" "/utf8>>),
_pipe@4 = gleam@string:replace(_pipe@3, <<"\r"/utf8>>, <<" "/utf8>>),
_pipe@5 = gleam@string:replace(_pipe@4, <<"."/utf8>>, <<" "/utf8>>),
_pipe@6 = gleam@string:replace(_pipe@5, <<":"/utf8>>, <<" "/utf8>>),
justin:snake_case(_pipe@6).
-spec new_destination(snapshot(new()), binary()) -> binary().
new_destination(Snapshot, Folder) ->
<<(filepath:join(Folder, file_name(erlang:element(2, Snapshot))))/binary,
".new"/utf8>>.
-spec strip_extension(binary()) -> binary().
strip_extension(File) ->
case filepath:extension(File) of
{ok, Extension} ->
gleam@string:drop_right(File, gleam@string:length(Extension) + 1);
{error, nil} ->
File
end.
-spec to_accepted_path(binary()) -> binary().
to_accepted_path(File) ->
<<(strip_extension(File))/binary, ".accepted"/utf8>>.
-spec accept_snapshot(binary()) -> {ok, nil} | {error, error()}.
accept_snapshot(New_snapshot_path) ->
Accepted_snapshot_path = to_accepted_path(New_snapshot_path),
_pipe = simplifile:rename_file(New_snapshot_path, Accepted_snapshot_path),
gleam@result:map_error(
_pipe,
fun(_capture) ->
{cannot_accept_snapshot, _capture, New_snapshot_path}
end
).
-spec explain(error()) -> nil.
explain(Error) ->
Heading = fun(Reason) ->
<<<<"["/utf8,
(gleam_community@ansi:bold(gleam@string:inspect(Reason)))/binary>>/binary,
"] "/utf8>>
end,
Message = case Error of
{cannot_create_snapshots_folder, Reason@1} ->
<<(Heading(Reason@1))/binary,
"I couldn't create the snapshots folder"/utf8>>;
{cannot_read_accepted_snapshot, Reason@2, Source} ->
<<<<(Heading(Reason@2))/binary,
"I couldn't read the accepted snapshot from "/utf8>>/binary,
(gleam_community@ansi:italic(
<<<<"\""/utf8, Source/binary>>/binary, "\"\n"/utf8>>
))/binary>>;
{cannot_read_new_snapshot, Reason@3, Source@1} ->
<<<<(Heading(Reason@3))/binary,
"I couldn't read the new snapshot from "/utf8>>/binary,
(gleam_community@ansi:italic(
<<<<"\""/utf8, Source@1/binary>>/binary, "\"\n"/utf8>>
))/binary>>;
{cannot_save_new_snapshot, Reason@4, Title, Destination} ->
<<<<<<<<(Heading(Reason@4))/binary,
"I couldn't save the snapshot "/utf8>>/binary,
(gleam_community@ansi:italic(
<<<<"\""/utf8, Title/binary>>/binary, "\" "/utf8>>
))/binary>>/binary,
"to "/utf8>>/binary,
(gleam_community@ansi:italic(
<<<<"\""/utf8, Destination/binary>>/binary, "\"\n"/utf8>>
))/binary>>;
{cannot_read_snapshots, Reason@5, _} ->
<<(Heading(Reason@5))/binary,
"I couldn't read the snapshots directory's contents"/utf8>>;
{cannot_reject_snapshot, Reason@6, Snapshot} ->
<<<<(Heading(Reason@6))/binary,
"I couldn't reject the snapshot"/utf8>>/binary,
(gleam_community@ansi:italic(
<<<<"\""/utf8, Snapshot/binary>>/binary, "\" "/utf8>>
))/binary>>;
{cannot_accept_snapshot, Reason@7, Snapshot@1} ->
<<<<(Heading(Reason@7))/binary,
"I couldn't accept the snapshot"/utf8>>/binary,
(gleam_community@ansi:italic(
<<<<"\""/utf8, Snapshot@1/binary>>/binary, "\" "/utf8>>
))/binary>>;
cannot_read_user_input ->
<<"I couldn't read the user input"/utf8>>;
{corrupted_snapshot, Source@2} ->
<<<<<<<<"It looks like "/utf8,
(gleam_community@ansi:italic(
<<<<"\""/utf8, Source@2/binary>>/binary,
"\"\n"/utf8>>
))/binary>>/binary,
" is not a valid snapshot.\n"/utf8>>/binary,
"This might happen when someone modifies its content.\n"/utf8>>/binary,
"Try deleting the snapshot and recreating it."/utf8>>;
{cannot_find_project_root, Reason@8} ->
<<<<(Heading(Reason@8))/binary,
"I couldn't locate the project's root where the snapshot's"/utf8>>/binary,
" folder should be."/utf8>>
end,
gleam@io:println_error(
<<"❌ "/utf8, (gleam_community@ansi:red(Message))/binary>>
).
-spec pretty_info_line(info_line(), integer()) -> binary().
pretty_info_line(Line, Width) ->
Title_length = case Line of
{info_line_with_no_title, _, _} ->
2;
{info_line_with_title, _, _, Title} ->
gleam@string:length(Title)
end,
Line_doc = case erlang:element(3, Line) of
do_not_split ->
glam@doc:from_string(erlang:element(2, Line));
split_words ->
_pipe = gleam@string:split(erlang:element(2, Line), <<"\n"/utf8>>),
_pipe@3 = gleam@list:map(
_pipe,
fun(Line@1) ->
_pipe@1 = gleam@string:split(Line@1, <<" "/utf8>>),
_pipe@2 = gleam@list:map(
_pipe@1,
fun glam@doc:from_string/1
),
glam@doc:join(
_pipe@2,
{flex_break, <<" "/utf8>>, <<""/utf8>>}
)
end
),
_pipe@4 = glam@doc:join(_pipe@3, {line, 1}),
_pipe@5 = glam@doc:group(_pipe@4),
glam@doc:nest(_pipe@5, Title_length + 4);
truncate ->
Max_content_length = (Width - Title_length) - 6,
Content_length = gleam@string:length(erlang:element(2, Line)),
case Content_length > Max_content_length of
false ->
glam@doc:from_string(erlang:element(2, Line));
true ->
_pipe@6 = gleam@string:to_graphemes(erlang:element(2, Line)),
_pipe@7 = gleam@list:take(_pipe@6, Max_content_length - 3),
_pipe@8 = gleam@string:join(_pipe@7, <<""/utf8>>),
_pipe@9 = gleam@string:append(_pipe@8, <<"..."/utf8>>),
glam@doc:from_string(_pipe@9)
end
end,
Ansi_code_len = 7,
_pipe@10 = case Line of
{info_line_with_no_title, _, _} ->
glam@doc:from_string(<<" "/utf8>>);
{info_line_with_title, _, _, Title@1} ->
glam@doc:from_string(
gleam_community@ansi:blue(
<<<<" "/utf8, Title@1/binary>>/binary, ": "/utf8>>
)
)
end,
_pipe@11 = glam@doc:append(_pipe@10, Line_doc),
glam@doc:to_string(_pipe@11, Width + Ansi_code_len).
-spec pretty_diff_line(birdie@internal@diff:diff_line(), integer()) -> binary().
pretty_diff_line(Diff_line, Padding) ->
{diff_line, Number, Line, Kind} = Diff_line,
{Pretty_number, Pretty_line, Separator} = case Kind of
shared ->
{begin
_pipe = gleam@int:to_string(Number),
_pipe@1 = gleam@string:pad_left(
_pipe,
Padding - 1,
<<" "/utf8>>
),
gleam_community@ansi:dim(_pipe@1)
end,
gleam_community@ansi:dim(Line),
<<" │ "/utf8>>};
new ->
{begin
_pipe@2 = gleam@int:to_string(Number),
_pipe@3 = gleam@string:pad_left(
_pipe@2,
Padding - 1,
<<" "/utf8>>
),
_pipe@4 = gleam_community@ansi:green(_pipe@3),
gleam_community@ansi:bold(_pipe@4)
end,
gleam_community@ansi:green(Line),
gleam_community@ansi:green(<<" + "/utf8>>)};
old ->
Number@1 = begin
_pipe@5 = (<<" "/utf8, (gleam@int:to_string(Number))/binary>>),
gleam@string:pad_right(_pipe@5, Padding - 1, <<" "/utf8>>)
end,
{gleam_community@ansi:red(Number@1),
gleam_community@ansi:red(Line),
gleam_community@ansi:red(<<" - "/utf8>>)}
end,
<<<<Pretty_number/binary, Separator/binary>>/binary, Pretty_line/binary>>.
-spec help_text() -> binary().
help_text() ->
<<<<<<<<<<<<<<<<<<<<(gleam_community@ansi:yellow(<<"USAGE:\n"/utf8>>))/binary,
" gleam run -m birdie [ <SUBCOMMAND> ]\n\n"/utf8>>/binary,
(gleam_community@ansi:yellow(
<<"SUBCOMMANDS:\n"/utf8>>
))/binary>>/binary,
(gleam_community@ansi:green(
<<" review "/utf8>>
))/binary>>/binary,
"Review all new snapshots one by one\n"/utf8>>/binary,
(gleam_community@ansi:green(
<<" accept-all "/utf8>>
))/binary>>/binary,
"Accept all new snapshots\n"/utf8>>/binary,
(gleam_community@ansi:green(<<" reject-all "/utf8>>))/binary>>/binary,
"Reject all new snapshots\n"/utf8>>/binary,
(gleam_community@ansi:green(<<" help "/utf8>>))/binary>>/binary,
"Show this help text\n"/utf8>>.
-spec unexpected_subcommand(binary()) -> nil.
unexpected_subcommand(Subcommand) ->
Error_message = <<<<<<(gleam_community@ansi:bold(<<"Error: "/utf8>>))/binary,
"\""/utf8>>/binary,
Subcommand/binary>>/binary,
"\" isn't a valid subcommand."/utf8>>,
gleam@io:println(
<<<<(gleam_community@ansi:red(Error_message))/binary, "\n\n"/utf8>>/binary,
(help_text())/binary>>
).
-spec more_than_one_command(list(binary())) -> nil.
more_than_one_command(Subcommands) ->
Error_message = <<<<(gleam_community@ansi:bold(<<"Error: "/utf8>>))/binary,
"I can only run one subcommand at a time, but more than one were provided: "/utf8>>/binary,
(gleam@string:join(
gleam@list:map(
Subcommands,
fun(S) -> <<<<"\""/utf8, S/binary>>/binary, "\""/utf8>> end
),
<<", "/utf8>>
))/binary>>,
gleam@io:println(
<<<<(gleam_community@ansi:red(Error_message))/binary, "\n\n"/utf8>>/binary,
(help_text())/binary>>
).
-spec report_status({ok, nil} | {error, error()}) -> nil.
report_status(Result) ->
case Result of
{ok, nil} ->
gleam@io:println(
gleam_community@ansi:green(<<"🐦⬛ Done!"/utf8>>)
);
{error, Error} ->
explain(Error)
end.
-spec ask_choice() -> {ok, review_choice()} | {error, error()}.
ask_choice() ->
gleam@io:println(
<<<<<<<<<<<<<<<<(gleam_community@ansi:bold(
gleam_community@ansi:green(
<<" a"/utf8>>
)
))/binary,
" accept "/utf8>>/binary,
(gleam_community@ansi:dim(
<<"accept the new snapshot\n"/utf8>>
))/binary>>/binary,
(gleam_community@ansi:bold(
gleam_community@ansi:red(<<" r"/utf8>>)
))/binary>>/binary,
" reject "/utf8>>/binary,
(gleam_community@ansi:dim(
<<"reject the new snapshot\n"/utf8>>
))/binary>>/binary,
(gleam_community@ansi:bold(
gleam_community@ansi:yellow(<<" s"/utf8>>)
))/binary>>/binary,
" skip "/utf8>>/binary,
(gleam_community@ansi:dim(<<"skip the snapshot for now\n"/utf8>>))/binary>>
),
birdie_ffi_erl:clear_line(),
case gleam@result:map(
gleam_erlang_ffi:get_line(<<"> "/utf8>>),
fun gleam@string:trim/1
) of
{ok, <<"a"/utf8>>} ->
{ok, accept_snapshot};
{ok, <<"r"/utf8>>} ->
{ok, reject_snapshot};
{ok, <<"s"/utf8>>} ->
{ok, skip_snapshot};
{ok, _} ->
birdie_ffi_erl:cursor_up(5),
ask_choice();
{error, _} ->
{error, cannot_read_user_input}
end.
-spec terminal_width() -> integer().
terminal_width() ->
gleam@result:unwrap(birdie_ffi_erl:terminal_width(), 80).
-spec pretty_box(
binary(),
list(birdie@internal@diff:diff_line()),
list(info_line())
) -> binary().
pretty_box(Title, Content_lines, Info_lines) ->
Width = terminal_width(),
_assert_subject = begin
Lines_count = gleam@list:length(Content_lines) + 1,
gleam@result:'try'(
gleam@int:digits(Lines_count, 10),
fun(Digits) -> {ok, (gleam@list:length(Digits) * 2) + 5} end
)
end,
{ok, Padding} = case _assert_subject of
{ok, _} -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"birdie"/utf8>>,
function => <<"pretty_box"/utf8>>,
line => 477})
end,
Title_length = gleam@string:length(Title),
Title_line_right = gleam@string:repeat(
<<"─"/utf8>>,
(Width - 5) - Title_length
),
Title_line = <<<<<<"── "/utf8, Title/binary>>/binary, " ─"/utf8>>/binary,
Title_line_right/binary>>,
Info_lines@1 = begin
_pipe = gleam@list:map(
Info_lines,
fun(_capture) -> pretty_info_line(_capture, Width) end
),
gleam@string:join(_pipe, <<"\n"/utf8>>)
end,
Content = begin
_pipe@1 = gleam@list:map(
Content_lines,
fun(_capture@1) -> pretty_diff_line(_capture@1, Padding) end
),
gleam@string:join(_pipe@1, <<"\n"/utf8>>)
end,
Left_padding_line = gleam@string:repeat(<<"─"/utf8>>, Padding),
Right_padding_line = gleam@string:repeat(
<<"─"/utf8>>,
(Width - Padding) - 1
),
Open_line = <<<<Left_padding_line/binary, "┬"/utf8>>/binary,
Right_padding_line/binary>>,
Closed_line = <<<<Left_padding_line/binary, "┴"/utf8>>/binary,
Right_padding_line/binary>>,
_pipe@2 = [Title_line,
<<""/utf8>>,
Info_lines@1,
<<""/utf8>>,
Open_line,
Content,
Closed_line],
gleam@string:join(_pipe@2, <<"\n"/utf8>>).
-spec new_snapshot_box(snapshot(new()), list(info_line())) -> binary().
new_snapshot_box(Snapshot, Additional_info_lines) ->
{snapshot, Title, Content} = Snapshot,
Content@1 = begin
_pipe = gleam@string:split(Content, <<"\n"/utf8>>),
gleam@list:index_map(
_pipe,
fun(Line, I) -> {diff_line, I + 1, Line, new} end
)
end,
pretty_box(
<<"new snapshot"/utf8>>,
Content@1,
[{info_line_with_title, Title, split_words, <<"title"/utf8>>} |
Additional_info_lines]
).
-spec diff_snapshot_box(
snapshot(accepted()),
snapshot(new()),
list(info_line())
) -> binary().
diff_snapshot_box(Accepted, New, Additional_info_lines) ->
pretty_box(
<<"mismatched snapshots"/utf8>>,
to_diff_lines(Accepted, New),
begin
_pipe = [[{info_line_with_title,
erlang:element(2, New),
split_words,
<<"title"/utf8>>}],
Additional_info_lines,
[{info_line_with_no_title, <<""/utf8>>, do_not_split},
{info_line_with_no_title,
gleam_community@ansi:red(<<"- old snapshot"/utf8>>),
do_not_split},
{info_line_with_no_title,
gleam_community@ansi:green(<<"+ new snapshot"/utf8>>),
do_not_split}]],
gleam@list:concat(_pipe)
end
).
-spec do_review(list(binary()), integer(), integer()) -> {ok, nil} |
{error, error()}.
do_review(New_snapshot_paths, Current, Out_of) ->
case New_snapshot_paths of
[] ->
{ok, nil};
[New_snapshot_path | Rest] ->
birdie_ffi_erl:clear(),
gleam@result:'try'(
read_new(New_snapshot_path),
fun(New_snapshot) ->
Accepted_snapshot_path = to_accepted_path(New_snapshot_path),
gleam@result:'try'(
read_accepted(Accepted_snapshot_path),
fun(Accepted_snapshot) ->
Progress = <<<<<<(gleam_community@ansi:dim(
<<"Reviewing "/utf8>>
))/binary,
(gleam_community@ansi:bold(
gleam_community@ansi:yellow(
rank:ordinalise(Current)
)
))/binary>>/binary,
(gleam_community@ansi:dim(
<<" out of "/utf8>>
))/binary>>/binary,
(gleam_community@ansi:bold(
gleam_community@ansi:yellow(
gleam@int:to_string(Out_of)
)
))/binary>>,
Box = case Accepted_snapshot of
none ->
new_snapshot_box(New_snapshot, []);
{some, Accepted_snapshot@1} ->
diff_snapshot_box(
Accepted_snapshot@1,
New_snapshot,
[]
)
end,
gleam@io:println(
<<<<<<Progress/binary, "\n\n"/utf8>>/binary,
Box/binary>>/binary,
"\n"/utf8>>
),
gleam@result:'try'(
ask_choice(),
fun(Choice) -> gleam@result:'try'(case Choice of
accept_snapshot ->
accept_snapshot(
New_snapshot_path
);
reject_snapshot ->
reject_snapshot(
New_snapshot_path
);
skip_snapshot ->
{ok, nil}
end, fun(_) ->
do_review(Rest, Current + 1, Out_of)
end) end
)
end
)
end
)
end.
-spec serialise(snapshot(new())) -> binary().
serialise(Snapshot) ->
{snapshot, Title, Content} = Snapshot,
_pipe = [<<"---"/utf8>>,
<<"version: "/utf8, "1.0.2"/utf8>>,
<<"title: "/utf8,
(gleam@string:replace(Title, <<"\n"/utf8>>, <<"\\n"/utf8>>))/binary>>,
<<"---"/utf8>>,
Content],
gleam@string:join(_pipe, <<"\n"/utf8>>).
-spec save(snapshot(new()), binary()) -> {ok, nil} | {error, error()}.
save(Snapshot, Destination) ->
case gleam@string:ends_with(Destination, <<".new"/utf8>>) of
false ->
erlang:error(#{gleam_error => panic,
message => <<"Looks like I've messed up something, all new snapshots should have the `.new` extension"/utf8>>,
module => <<"birdie"/utf8>>,
function => <<"save"/utf8>>,
line => 219});
true ->
_pipe = simplifile:write(Destination, serialise(Snapshot)),
gleam@result:map_error(
_pipe,
fun(_capture) ->
{cannot_save_new_snapshot,
_capture,
erlang:element(2, Snapshot),
Destination}
end
)
end.
-spec help() -> nil.
help() ->
Version = <<<<(gleam_community@ansi:green(<<"🐦⬛ birdie "/utf8>>))/binary,
"v"/utf8>>/binary,
"1.0.2"/utf8>>,
gleam@io:println(
<<<<Version/binary, "\n\n"/utf8>>/binary, (help_text())/binary>>
).
-spec find_snapshots_folder() -> {ok, binary()} | {error, error()}.
find_snapshots_folder() ->
Result = gleam@result:map_error(
find_project_root(<<"."/utf8>>),
fun(Field@0) -> {cannot_find_project_root, Field@0} end
),
gleam@result:'try'(
Result,
fun(Project_root) ->
Snapshots_folder = filepath:join(
Project_root,
<<"birdie_snapshots"/utf8>>
),
case simplifile:create_directory(Snapshots_folder) of
{ok, nil} ->
{ok, Snapshots_folder};
{error, eexist} ->
{ok, Snapshots_folder};
{error, Error} ->
{error, {cannot_create_snapshots_folder, Error}}
end
end
).
-spec do_snap(binary(), binary()) -> {ok, outcome()} | {error, error()}.
do_snap(Content, Title) ->
gleam@result:'try'(
find_snapshots_folder(),
fun(Folder) ->
New = {snapshot, Title, Content},
New_snapshot_path = new_destination(New, Folder),
Accepted_snapshot_path = to_accepted_path(New_snapshot_path),
gleam@result:'try'(
read_accepted(Accepted_snapshot_path),
fun(Accepted) -> case Accepted of
none ->
gleam@result:'try'(
save(New, New_snapshot_path),
fun(_) ->
{ok,
{new_snapshot_created,
New,
New_snapshot_path}}
end
);
{some, Accepted@1} ->
gleam@bool:guard(
erlang:element(3, Accepted@1) =:= erlang:element(
3,
New
),
{ok, same},
fun() ->
gleam@result:'try'(
save(New, New_snapshot_path),
fun(_) ->
{ok, {different, Accepted@1, New}}
end
)
end
)
end end
)
end
).
-spec review() -> {ok, nil} | {error, error()}.
review() ->
gleam@result:'try'(
find_snapshots_folder(),
fun(Snapshots_folder) ->
gleam@result:'try'(
list_new_snapshots(Snapshots_folder),
fun(New_snapshots) -> case gleam@list:length(New_snapshots) of
0 ->
gleam@io:println(
<<"No new snapshots to review."/utf8>>
),
{ok, nil};
N ->
Result = do_review(New_snapshots, 1, N),
birdie_ffi_erl:clear(),
gleam@result:'try'(
Result,
fun(_) ->
gleam@io:println(case N of
1 ->
<<"Reviewed one snapshot"/utf8>>;
N@1 ->
<<<<"Reviewed "/utf8,
(gleam@int:to_string(
N@1
))/binary>>/binary,
" snapshots"/utf8>>
end),
{ok, nil}
end
)
end end
)
end
).
-spec accept_all() -> {ok, nil} | {error, error()}.
accept_all() ->
gleam@io:println(<<"Looking for new snapshots..."/utf8>>),
gleam@result:'try'(
find_snapshots_folder(),
fun(Snapshots_folder) ->
gleam@result:'try'(
list_new_snapshots(Snapshots_folder),
fun(New_snapshots) ->
case gleam@list:length(New_snapshots) of
0 ->
gleam@io:println(
<<"No new snapshots to accept."/utf8>>
);
1 ->
gleam@io:println(
<<"Accepting one new snapshot."/utf8>>
);
N ->
gleam@io:println(
<<<<"Accepting "/utf8,
(gleam@int:to_string(N))/binary>>/binary,
" new snapshots."/utf8>>
)
end,
gleam@list:try_each(New_snapshots, fun accept_snapshot/1)
end
)
end
).
-spec reject_all() -> {ok, nil} | {error, error()}.
reject_all() ->
gleam@io:println(<<"Looking for new snapshots..."/utf8>>),
gleam@result:'try'(
find_snapshots_folder(),
fun(Snapshots_folder) ->
gleam@result:'try'(
list_new_snapshots(Snapshots_folder),
fun(New_snapshots) ->
case gleam@list:length(New_snapshots) of
0 ->
gleam@io:println(
<<"No new snapshots to reject."/utf8>>
);
1 ->
gleam@io:println(
<<"Rejecting one new snapshot."/utf8>>
);
N ->
gleam@io:println(
<<<<"Rejecting "/utf8,
(gleam@int:to_string(N))/binary>>/binary,
" new snapshots."/utf8>>
)
end,
gleam@list:try_each(New_snapshots, fun reject_snapshot/1)
end
)
end
).
-spec main() -> nil.
main() ->
case erlang:element(4, argv:load()) of
[] ->
report_status(review());
[<<"review"/utf8>>] ->
report_status(review());
[<<"accept-all"/utf8>>] ->
report_status(accept_all());
[<<"accept"/utf8>>, <<"all"/utf8>>] ->
report_status(accept_all());
[<<"reject-all"/utf8>>] ->
report_status(reject_all());
[<<"reject"/utf8>>, <<"all"/utf8>>] ->
report_status(reject_all());
[<<"help"/utf8>>] ->
help();
[Subcommand] ->
unexpected_subcommand(Subcommand);
Subcommands ->
more_than_one_command(Subcommands)
end.
-spec snap(binary(), binary()) -> nil.
snap(Content, Title) ->
case do_snap(Content, Title) of
{ok, same} ->
nil;
{ok, {new_snapshot_created, Snapshot, _}} ->
Hint_message = gleam_community@ansi:yellow(
<<"run `gleam run -m birdie` to review the snapshots"/utf8>>
),
Hint = {info_line_with_title,
Hint_message,
do_not_split,
<<"hint"/utf8>>},
Box = new_snapshot_box(Snapshot, [Hint]),
gleam@io:println_error(
<<<<"\n\n"/utf8, Box/binary>>/binary, "\n"/utf8>>
),
gleam@io:println(<<"🐦⬛ Birdie snapshot test failed"/utf8>>),
gleeunit@should:fail();
{ok, {different, Accepted, New}} ->
Hint_message@1 = gleam_community@ansi:yellow(
<<"run `gleam run -m birdie` to review the snapshots"/utf8>>
),
Hint@1 = {info_line_with_title,
Hint_message@1,
do_not_split,
<<"hint"/utf8>>},
Box@1 = diff_snapshot_box(Accepted, New, [Hint@1]),
gleam@io:println_error(
<<<<"\n\n"/utf8, Box@1/binary>>/binary, "\n"/utf8>>
),
gleam@io:println(<<"🐦⬛ Birdie snapshot test failed"/utf8>>),
gleeunit@should:fail();
{error, Error} ->
explain(Error),
gleam@io:println(<<"🐦⬛ Birdie snapshot test failed"/utf8>>),
gleeunit@should:fail()
end.