-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, <<<>/binary, Pretty_line/binary>>. -spec help_text() -> binary(). help_text() -> <<<<<<<<<<<<<<<<<<<<(gleam_community@ansi:yellow(<<"USAGE:\n"/utf8>>))/binary, " gleam run -m birdie [ ]\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 = <<<>/binary, Right_padding_line/binary>>, Closed_line = <<<>/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( <<<<<>/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( <<<>/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.