src/automata@cron@parser.erl

-module(automata@cron@parser).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/automata/cron/parser.gleam").
-export([parse/1]).
-export_type([parse_error/0]).

-type parse_error() :: {invalid_expression, binary()} |
    {invalid_field_count, integer(), integer()} |
    {empty_field, automata@cron@ast:field()} |
    {unsupported_syntax, automata@cron@ast:field(), binary()}.

-file("src/automata/cron/parser.gleam", 53).
-spec first_empty_field(list(binary())) -> gleam@option:option(automata@cron@ast:field()).
first_empty_field(Fields) ->
    case Fields of
        [Minute, Hour, Day_of_month, Month, Day_of_week] ->
            case Minute =:= <<""/utf8>> of
                true ->
                    {some, minute};

                false ->
                    case Hour =:= <<""/utf8>> of
                        true ->
                            {some, hour};

                        false ->
                            case Day_of_month =:= <<""/utf8>> of
                                true ->
                                    {some, day_of_month};

                                false ->
                                    case Month =:= <<""/utf8>> of
                                        true ->
                                            {some, month};

                                        false ->
                                            case Day_of_week =:= <<""/utf8>> of
                                                true ->
                                                    {some, day_of_week};

                                                false ->
                                                    none
                                            end
                                    end
                            end
                    end
            end;

        _ ->
            none
    end.

-file("src/automata/cron/parser.gleam", 97).
-spec flush_split_parts({list(binary()), binary()}) -> list(binary()).
flush_split_parts(Parts) ->
    {Collected, Current} = Parts,
    case Current =:= <<""/utf8>> of
        true ->
            lists:reverse(Collected);

        false ->
            lists:reverse([Current | Collected])
    end.

-file("src/automata/cron/parser.gleam", 106).
-spec is_whitespace(binary()) -> boolean().
is_whitespace(Grapheme) ->
    case Grapheme of
        <<" "/utf8>> ->
            true;

        <<"\t"/utf8>> ->
            true;

        <<"\n"/utf8>> ->
            true;

        <<"\r"/utf8>> ->
            true;

        _ ->
            false
    end.

-file("src/automata/cron/parser.gleam", 80).
-spec split_whitespace(binary()) -> list(binary()).
split_whitespace(Input) ->
    _pipe = Input,
    _pipe@1 = gleam@string:to_graphemes(_pipe),
    _pipe@2 = gleam@list:fold(
        _pipe@1,
        {[], <<""/utf8>>},
        fun(Acc, Grapheme) ->
            {Parts, Current} = Acc,
            case is_whitespace(Grapheme) of
                true ->
                    case Current =:= <<""/utf8>> of
                        true ->
                            {Parts, <<""/utf8>>};

                        false ->
                            {[Current | Parts], <<""/utf8>>}
                    end;

                false ->
                    {Parts, <<Current/binary, Grapheme/binary>>}
            end
        end
    ),
    flush_split_parts(_pipe@2).

-file("src/automata/cron/parser.gleam", 16).
-spec parse(binary()) -> {ok, automata@cron@ast:raw_cron()} |
    {error, parse_error()}.
parse(Input) ->
    Trimmed = gleam@string:trim(Input),
    case Trimmed of
        <<""/utf8>> ->
            {error, {invalid_expression, Input}};

        _ ->
            case gleam_stdlib:string_starts_with(Trimmed, <<"@"/utf8>>) of
                true ->
                    {error, {unsupported_syntax, expression, Trimmed}};

                false ->
                    case split_whitespace(Trimmed) of
                        [Minute, Hour, Day_of_month, Month, Day_of_week] ->
                            case first_empty_field(
                                [Minute, Hour, Day_of_month, Month, Day_of_week]
                            ) of
                                {some, Field} ->
                                    {error, {empty_field, Field}};

                                none ->
                                    {ok,
                                        {raw_cron,
                                            Minute,
                                            Hour,
                                            Day_of_month,
                                            Month,
                                            Day_of_week}}
                            end;

                        Fields ->
                            {error,
                                {invalid_field_count, 5, erlang:length(Fields)}}
                    end
            end
    end.