src/gleeam_code@list_cmd.erl

-module(gleeam_code@list_cmd).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gleeam_code/list_cmd.gleam").
-export([parse_filters/1, apply_filters/2, run/3]).
-export_type([problem_entry/0, filter/0, option/0]).

-type problem_entry() :: {problem_entry,
        integer(),
        binary(),
        binary(),
        binary()}.

-type filter() :: {filter, list(binary()), option(), option()}.

-type option() :: on | off.

-file("src/gleeam_code/list_cmd.gleam", 25).
-spec do_parse_filters(list(binary()), filter()) -> filter().
do_parse_filters(Args, Acc) ->
    case Args of
        [] ->
            Acc;

        [<<"--easy"/utf8>> | Rest] ->
            do_parse_filters(
                Rest,
                {filter,
                    [<<"Easy"/utf8>> | erlang:element(2, Acc)],
                    erlang:element(3, Acc),
                    erlang:element(4, Acc)}
            );

        [<<"--medium"/utf8>> | Rest@1] ->
            do_parse_filters(
                Rest@1,
                {filter,
                    [<<"Medium"/utf8>> | erlang:element(2, Acc)],
                    erlang:element(3, Acc),
                    erlang:element(4, Acc)}
            );

        [<<"--hard"/utf8>> | Rest@2] ->
            do_parse_filters(
                Rest@2,
                {filter,
                    [<<"Hard"/utf8>> | erlang:element(2, Acc)],
                    erlang:element(3, Acc),
                    erlang:element(4, Acc)}
            );

        [<<"--solved"/utf8>> | Rest@3] ->
            do_parse_filters(
                Rest@3,
                {filter, erlang:element(2, Acc), on, erlang:element(4, Acc)}
            );

        [<<"--unsolved"/utf8>> | Rest@4] ->
            do_parse_filters(
                Rest@4,
                {filter, erlang:element(2, Acc), erlang:element(3, Acc), on}
            );

        [_ | Rest@5] ->
            do_parse_filters(Rest@5, Acc)
    end.

-file("src/gleeam_code/list_cmd.gleam", 21).
-spec parse_filters(list(binary())) -> filter().
parse_filters(Args) ->
    do_parse_filters(Args, {filter, [], off, off}).

-file("src/gleeam_code/list_cmd.gleam", 59).
-spec filter_by_difficulty(list(problem_entry()), list(binary())) -> list(problem_entry()).
filter_by_difficulty(Problems, Difficulties) ->
    case Difficulties of
        [] ->
            Problems;

        _ ->
            gleam@list:filter(
                Problems,
                fun(P) ->
                    gleam@list:contains(Difficulties, erlang:element(4, P))
                end
            )
    end.

-file("src/gleeam_code/list_cmd.gleam", 70).
-spec filter_by_status(list(problem_entry()), option(), option()) -> list(problem_entry()).
filter_by_status(Problems, Solved, Unsolved) ->
    case {Solved, Unsolved} of
        {on, off} ->
            gleam@list:filter(
                Problems,
                fun(P) -> erlang:element(5, P) =:= <<"Accepted"/utf8>> end
            );

        {off, on} ->
            gleam@list:filter(
                Problems,
                fun(P@1) -> erlang:element(5, P@1) /= <<"Accepted"/utf8>> end
            );

        {_, _} ->
            Problems
    end.

-file("src/gleeam_code/list_cmd.gleam", 50).
-spec apply_filters(list(problem_entry()), filter()) -> list(problem_entry()).
apply_filters(Problems, Filter) ->
    _pipe = Problems,
    _pipe@1 = filter_by_difficulty(_pipe, erlang:element(2, Filter)),
    filter_by_status(
        _pipe@1,
        erlang:element(3, Filter),
        erlang:element(4, Filter)
    ).

-file("src/gleeam_code/list_cmd.gleam", 153).
-spec extract_number(list(binary())) -> {ok, integer()} | {error, nil}.
extract_number(Lines) ->
    case Lines of
        [] ->
            {error, nil};

        [Line | Rest] ->
            case gleam_stdlib:string_starts_with(Line, <<"//// Problem "/utf8>>) of
                true ->
                    After = gleam@string:drop_start(
                        Line,
                        string:length(<<"//// Problem "/utf8>>)
                    ),
                    case gleam@string:split_once(After, <<":"/utf8>>) of
                        {ok, {Num_str, _}} ->
                            _pipe = Num_str,
                            _pipe@1 = gleam@string:trim(_pipe),
                            _pipe@2 = gleam_stdlib:parse_int(_pipe@1),
                            gleam@result:replace_error(_pipe@2, nil);

                        {error, _} ->
                            {error, nil}
                    end;

                false ->
                    extract_number(Rest)
            end
    end.

-file("src/gleeam_code/list_cmd.gleam", 171).
-spec extract_slug(binary()) -> binary().
extract_slug(Dir_name) ->
    case gleam@string:split_once(Dir_name, <<"_"/utf8>>) of
        {ok, {_, Rest}} ->
            gleam@string:replace(Rest, <<"_"/utf8>>, <<"-"/utf8>>);

        {error, _} ->
            Dir_name
    end.

-file("src/gleeam_code/list_cmd.gleam", 178).
-spec extract_difficulty(list(binary())) -> binary().
extract_difficulty(Lines) ->
    case Lines of
        [] ->
            <<"?"/utf8>>;

        [Line | Rest] ->
            case gleam_stdlib:string_starts_with(
                Line,
                <<"//// Difficulty: "/utf8>>
            ) of
                true ->
                    _pipe = gleam@string:drop_start(
                        Line,
                        string:length(<<"//// Difficulty: "/utf8>>)
                    ),
                    gleam@string:trim(_pipe);

                false ->
                    extract_difficulty(Rest)
            end
    end.

-file("src/gleeam_code/list_cmd.gleam", 191).
-spec read_status(binary()) -> binary().
read_status(Meta_path) ->
    case gleeam_code@internal@file:read(Meta_path) of
        {ok, Content} ->
            gleeam_code@internal@meta:find_value(
                gleam@string:split(Content, <<"\n"/utf8>>),
                <<"status"/utf8>>
            );

        {error, _} ->
            <<""/utf8>>
    end.

-file("src/gleeam_code/list_cmd.gleam", 135).
-spec parse_solution_header(binary(), binary(), binary()) -> {ok,
        problem_entry()} |
    {error, nil}.
parse_solution_header(Content, Dir_name, Meta_path) ->
    Lines = gleam@string:split(Content, <<"\n"/utf8>>),
    gleam@result:'try'(
        extract_number(Lines),
        fun(Number) ->
            Slug = extract_slug(Dir_name),
            Difficulty = extract_difficulty(Lines),
            Status = read_status(Meta_path),
            {ok, {problem_entry, Number, Slug, Difficulty, Status}}
        end
    ).

-file("src/gleeam_code/list_cmd.gleam", 123).
-spec parse_entry(binary(), binary()) -> {ok, problem_entry()} | {error, nil}.
parse_entry(Solutions_dir, Dir_name) ->
    Solution_path = <<<<<<Solutions_dir/binary, "/"/utf8>>/binary,
            Dir_name/binary>>/binary,
        "/solution.gleam"/utf8>>,
    Meta_path = <<<<<<Solutions_dir/binary, "/"/utf8>>/binary, Dir_name/binary>>/binary,
        "/.glc_meta"/utf8>>,
    case gleeam_code@internal@file:read(Solution_path) of
        {ok, Content} ->
            parse_solution_header(Content, Dir_name, Meta_path);

        {error, _} ->
            {error, nil}
    end.

-file("src/gleeam_code/list_cmd.gleam", 212).
-spec format_status(binary()) -> binary().
format_status(Status) ->
    case Status of
        <<"Accepted"/utf8>> ->
            <<"✓ Accepted"/utf8>>;

        <<""/utf8>> ->
            <<""/utf8>>;

        Other ->
            <<"✗ "/utf8, Other/binary>>
    end.

-file("src/gleeam_code/list_cmd.gleam", 220).
-spec pad_right(binary(), integer()) -> binary().
pad_right(S, Width) ->
    Len = string:length(S),
    case Len >= Width of
        true ->
            S;

        false ->
            <<S/binary,
                (gleam@string:repeat(<<" "/utf8>>, Width - Len))/binary>>
    end.

-file("src/gleeam_code/list_cmd.gleam", 198).
-spec format_header() -> binary().
format_header() ->
    <<<<<<(pad_right(<<"#"/utf8>>, 5))/binary,
                (pad_right(<<"Slug"/utf8>>, 30))/binary>>/binary,
            (pad_right(<<"Difficulty"/utf8>>, 12))/binary>>/binary,
        "Status"/utf8>>.

-file("src/gleeam_code/list_cmd.gleam", 205).
-spec format_row(problem_entry()) -> binary().
format_row(Entry) ->
    <<<<<<(pad_right(erlang:integer_to_binary(erlang:element(2, Entry)), 5))/binary,
                (pad_right(erlang:element(3, Entry), 30))/binary>>/binary,
            (pad_right(erlang:element(4, Entry), 12))/binary>>/binary,
        (format_status(erlang:element(5, Entry)))/binary>>.

-file("src/gleeam_code/list_cmd.gleam", 82).
-spec run(binary(), list(binary()), fun((binary()) -> nil)) -> {ok, nil} |
    {error, binary()}.
run(Base_dir, Args, Print) ->
    Filter = parse_filters(Args),
    Solutions_dir = <<Base_dir/binary, "/src/solutions"/utf8>>,
    case gleeam_code@internal@file:dir_exists(Solutions_dir) of
        false ->
            Print(<<"No solutions found. Run `glc fetch` first."/utf8>>),
            {ok, nil};

        true ->
            gleam@result:'try'(
                begin
                    _pipe = gleeam_code@internal@file:list_directory(
                        Solutions_dir
                    ),
                    gleam@result:map_error(
                        _pipe,
                        fun(Err) ->
                            <<"Failed to read solutions directory: "/utf8,
                                (gleeam_code@internal@file:describe_error(Err))/binary>>
                        end
                    )
                end,
                fun(Entries) ->
                    Problems = begin
                        _pipe@1 = Entries,
                        _pipe@2 = gleam@list:filter_map(
                            _pipe@1,
                            fun(Entry) -> parse_entry(Solutions_dir, Entry) end
                        ),
                        _pipe@3 = gleam@list:sort(
                            _pipe@2,
                            fun(A, B) ->
                                gleam@int:compare(
                                    erlang:element(2, A),
                                    erlang:element(2, B)
                                )
                            end
                        ),
                        apply_filters(_pipe@3, Filter)
                    end,
                    case Problems of
                        [] ->
                            Print(
                                <<"No solutions found. Run `glc fetch` first."/utf8>>
                            ),
                            {ok, nil};

                        _ ->
                            Print(format_header()),
                            gleam@list:each(
                                Problems,
                                fun(P) -> Print(format_row(P)) end
                            ),
                            {ok, nil}
                    end
                end
            )
    end.