Skip to main content

src/molt@internal@classifier.erl

-module(molt@internal@classifier).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/molt/internal/classifier.gleam").
-export([match_datetime/1, match_type/1]).

-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.

?MODULEDOC(false).

-file("src/molt/internal/classifier.gleam", 258).
?DOC(false).
-spec do_take_digits(binary(), list(binary()), fun((binary()) -> boolean())) -> {boolean(),
    binary(),
    binary()}.
do_take_digits(Text, Acc, Predicate) ->
    gleam@bool:lazy_guard(
        Text =:= <<""/utf8>>,
        fun() ->
            {true, molt@internal@utils:reverse_concat(Acc), <<""/utf8>>}
        end,
        fun() -> case molt@internal@utils:split_at(Text, 1) of
                {<<"_"/utf8>>, <<""/utf8>>} ->
                    {false, <<""/utf8>>, <<""/utf8>>};

                {<<"_"/utf8>>, <<"_"/utf8, _/binary>>} ->
                    {false, <<""/utf8>>, <<""/utf8>>};

                {<<"_"/utf8>>, Rest} ->
                    gleam@bool:guard(
                        not Predicate(gleam@string:slice(Rest, 0, 1)),
                        {false, <<""/utf8>>, <<""/utf8>>},
                        fun() -> do_take_digits(Rest, Acc, Predicate) end
                    );

                {First, Rest@1} ->
                    gleam@bool:guard(
                        Predicate(First),
                        do_take_digits(Rest@1, [First | Acc], Predicate),
                        fun() ->
                            {true,
                                molt@internal@utils:reverse_concat(Acc),
                                Text}
                        end
                    )
            end end
    ).

-file("src/molt/internal/classifier.gleam", 245).
?DOC(false).
-spec take_digits(binary()) -> {boolean(), binary(), binary()}.
take_digits(Text) ->
    gleam@bool:guard(
        Text =:= <<""/utf8>>,
        {false, <<""/utf8>>, <<""/utf8>>},
        fun() ->
            {First, Rest} = molt@internal@utils:split_at(Text, 1),
            gleam@bool:guard(
                begin
                    _pipe = casefold:is_decimal_grapheme(First),
                    gleam@bool:negate(_pipe)
                end,
                {false, <<""/utf8>>, <<""/utf8>>},
                fun() ->
                    do_take_digits(
                        Rest,
                        [First],
                        fun casefold:is_decimal_grapheme/1
                    )
                end
            )
        end
    ).

-file("src/molt/internal/classifier.gleam", 296).
?DOC(false).
-spec match_exponent(binary()) -> gleam@option:option(molt@types:toml_kind()).
match_exponent(Text) ->
    Text@1 = case Text of
        <<"+"/utf8, Rest/binary>> ->
            Rest;

        <<"-"/utf8, Rest/binary>> ->
            Rest;

        _ ->
            Text
    end,
    gleam@bool:guard(
        Text@1 =:= <<""/utf8>>,
        none,
        fun() ->
            gleam@bool:guard(
                begin
                    _pipe = gleam@string:slice(Text@1, 0, 1),
                    _pipe@1 = casefold:is_decimal_grapheme(_pipe),
                    gleam@bool:negate(_pipe@1)
                end,
                none,
                fun() -> case take_digits(Text@1) of
                        {true, _, <<""/utf8>>} ->
                            {some, float};

                        _ ->
                            none
                    end end
            )
        end
    ).

-file("src/molt/internal/classifier.gleam", 288).
?DOC(false).
-spec has_invalid_leading_zero(binary()) -> boolean().
has_invalid_leading_zero(Text) ->
    case Text of
        <<"0"/utf8>> ->
            false;

        <<"0"/utf8, _/binary>> ->
            true;

        _ ->
            false
    end.

-file("src/molt/internal/classifier.gleam", 78).
?DOC(false).
-spec match_decimal(binary()) -> gleam@option:option(molt@types:toml_kind()).
match_decimal(Text) ->
    Text@1 = case Text of
        <<"+"/utf8, Rest/binary>> ->
            Rest;

        <<"-"/utf8, Rest/binary>> ->
            Rest;

        _ ->
            Text
    end,
    gleam@bool:guard(
        Text@1 =:= <<""/utf8>>,
        none,
        fun() ->
            gleam@bool:guard(
                begin
                    _pipe = gleam@string:slice(Text@1, 0, 1),
                    _pipe@1 = casefold:is_decimal_grapheme(_pipe),
                    gleam@bool:negate(_pipe@1)
                end,
                none,
                fun() ->
                    {Valid, Integer, Decimal} = take_digits(Text@1),
                    gleam@bool:guard(
                        not Valid,
                        none,
                        fun() ->
                            gleam@bool:guard(
                                has_invalid_leading_zero(Integer),
                                none,
                                fun() -> case Decimal of
                                        <<""/utf8>> ->
                                            {some, integer};

                                        <<"."/utf8>> ->
                                            none;

                                        <<"."/utf8, Fraction/binary>> ->
                                            {Valid@1, Fraction@1, Exponent} = take_digits(
                                                Fraction
                                            ),
                                            gleam@bool:guard(
                                                not Valid@1,
                                                none,
                                                fun() ->
                                                    gleam@bool:guard(
                                                        Fraction@1 =:= <<""/utf8>>,
                                                        none,
                                                        fun() ->
                                                            case Exponent of
                                                                <<""/utf8>> ->
                                                                    {some,
                                                                        float};

                                                                <<"e"/utf8, Exponent@1/binary>> ->
                                                                    match_exponent(
                                                                        Exponent@1
                                                                    );

                                                                <<"E"/utf8, Exponent@1/binary>> ->
                                                                    match_exponent(
                                                                        Exponent@1
                                                                    );

                                                                _ ->
                                                                    none
                                                            end
                                                        end
                                                    )
                                                end
                                            );

                                        <<"e"/utf8, Exponent@2/binary>> ->
                                            match_exponent(Exponent@2);

                                        <<"E"/utf8, Exponent@2/binary>> ->
                                            match_exponent(Exponent@2);

                                        _ ->
                                            none
                                    end end
                            )
                        end
                    )
                end
            )
        end
    ).

-file("src/molt/internal/classifier.gleam", 233).
?DOC(false).
-spec do_match_digits(binary(), fun((binary()) -> boolean())) -> boolean().
do_match_digits(Text, Predicate) ->
    gleam@bool:guard(
        Text =:= <<""/utf8>>,
        true,
        fun() -> case molt@internal@utils:split_at(Text, 1) of
                {<<"_"/utf8>>, <<""/utf8>>} ->
                    false;

                {<<"_"/utf8>>, <<"_"/utf8, _/binary>>} ->
                    false;

                {<<"_"/utf8>>, Rest} ->
                    do_match_digits(Rest, Predicate);

                {First, Rest@1} ->
                    Predicate(First) andalso do_match_digits(Rest@1, Predicate)
            end end
    ).

-file("src/molt/internal/classifier.gleam", 227).
?DOC(false).
-spec match_digits(binary(), fun((binary()) -> boolean())) -> boolean().
match_digits(Text, Predicate) ->
    gleam@bool:guard(
        Text =:= <<""/utf8>>,
        false,
        fun() ->
            {First, Rest} = molt@internal@utils:split_at(Text, 1),
            Predicate(First) andalso do_match_digits(Rest, Predicate)
        end
    ).

-file("src/molt/internal/classifier.gleam", 51).
?DOC(false).
-spec match_number(binary()) -> gleam@option:option(molt@types:toml_kind()).
match_number(Text) ->
    case Text of
        <<"0x"/utf8, Rest/binary>> ->
            case match_digits(Rest, fun casefold:is_hex_grapheme/1) of
                true ->
                    {some, hex_integer};

                false ->
                    none
            end;

        <<"0o"/utf8, Rest@1/binary>> ->
            case match_digits(Rest@1, fun casefold:is_octal_grapheme/1) of
                true ->
                    {some, octal_integer};

                false ->
                    none
            end;

        <<"0b"/utf8, Rest@2/binary>> ->
            case match_digits(Rest@2, fun casefold:is_binary_grapheme/1) of
                true ->
                    {some, binary_integer};

                false ->
                    none
            end;

        _ ->
            match_decimal(Text)
    end.

-file("src/molt/internal/classifier.gleam", 213).
?DOC(false).
-spec valid_time_fraction(binary()) -> boolean().
valid_time_fraction(Fraction) ->
    case Fraction of
        <<""/utf8>> ->
            true;

        <<"."/utf8>> ->
            false;

        <<"."/utf8, Fraction@1/binary>> ->
            casefold:is_decimal(Fraction@1);

        _ ->
            false
    end.

-file("src/molt/internal/classifier.gleam", 192).
?DOC(false).
-spec match_time_parts(binary(), binary(), binary(), binary()) -> boolean().
match_time_parts(Hour, Minute, Second, Fraction) ->
    gleam@bool:guard(
        string:length(Hour) /= 2,
        false,
        fun() ->
            Hour@1 = begin
                _pipe = gleam_stdlib:parse_int(Hour),
                gleam@result:unwrap(_pipe, -1)
            end,
            gleam@bool:guard(
                (Hour@1 < 0) orelse (Hour@1 > 23),
                false,
                fun() ->
                    gleam@bool:guard(
                        string:length(Minute) /= 2,
                        false,
                        fun() ->
                            Minute@1 = begin
                                _pipe@1 = gleam_stdlib:parse_int(Minute),
                                gleam@result:unwrap(_pipe@1, -1)
                            end,
                            gleam@bool:guard(
                                (Minute@1 < 0) orelse (Minute@1 > 59),
                                false,
                                fun() ->
                                    gleam@bool:guard(
                                        Second =:= <<""/utf8>>,
                                        true,
                                        fun() ->
                                            gleam@bool:guard(
                                                string:length(Second) /= 2,
                                                false,
                                                fun() ->
                                                    Second@1 = begin
                                                        _pipe@2 = gleam_stdlib:parse_int(
                                                            Second
                                                        ),
                                                        gleam@result:unwrap(
                                                            _pipe@2,
                                                            -1
                                                        )
                                                    end,
                                                    ((Second@1 >= 0) andalso (Second@1
                                                    =< 60))
                                                    andalso valid_time_fraction(
                                                        Fraction
                                                    )
                                                end
                                            )
                                        end
                                    )
                                end
                            )
                        end
                    )
                end
            )
        end
    ).

-file("src/molt/internal/classifier.gleam", 181).
?DOC(false).
-spec match_local_time(binary()) -> boolean().
match_local_time(Text) ->
    case gleam@string:split(Text, <<":"/utf8>>) of
        [Hour, Minute] ->
            match_time_parts(Hour, Minute, <<""/utf8>>, <<""/utf8>>);

        [Hour@1, Minute@1, Second] ->
            {Second@1, Fraction} = molt@internal@utils:split_at(Second, 2),
            match_time_parts(Hour@1, Minute@1, Second@1, Fraction);

        _ ->
            false
    end.

-file("src/molt/internal/classifier.gleam", 360).
?DOC(false).
-spec is_leap(integer()) -> boolean().
is_leap(Year) ->
    gleam@bool:guard(
        (Year rem 4) /= 0,
        false,
        fun() ->
            gleam@bool:guard(
                (Year rem 100) /= 0,
                true,
                fun() -> (Year rem 400) =:= 0 end
            )
        end
    ).

-file("src/molt/internal/classifier.gleam", 347).
?DOC(false).
-spec days_in_month(integer(), integer()) -> integer().
days_in_month(Year, Month) ->
    case Month of
        1 ->
            31;

        3 ->
            31;

        5 ->
            31;

        7 ->
            31;

        8 ->
            31;

        10 ->
            31;

        12 ->
            31;

        4 ->
            30;

        6 ->
            30;

        9 ->
            30;

        11 ->
            30;

        2 ->
            case is_leap(Year) of
                true ->
                    29;

                false ->
                    28
            end;

        _ ->
            0
    end.

-file("src/molt/internal/classifier.gleam", 159).
?DOC(false).
-spec valid_date_parts(binary(), binary(), binary()) -> boolean().
valid_date_parts(Year, Month, Day) ->
    gleam@bool:guard(
        string:length(Year) /= 4,
        false,
        fun() ->
            gleam@bool:guard(
                string:length(Month) /= 2,
                false,
                fun() ->
                    gleam@bool:guard(
                        string:length(Day) /= 2,
                        false,
                        fun() ->
                            Year@1 = begin
                                _pipe = gleam_stdlib:parse_int(Year),
                                gleam@result:unwrap(_pipe, -1)
                            end,
                            Month@1 = begin
                                _pipe@1 = gleam_stdlib:parse_int(Month),
                                gleam@result:unwrap(_pipe@1, 0)
                            end,
                            Day@1 = begin
                                _pipe@2 = gleam_stdlib:parse_int(Day),
                                gleam@result:unwrap(_pipe@2, 0)
                            end,
                            (((((Year@1 >= 0) andalso (Year@1 < 10000)) andalso (Month@1
                            >= 1))
                            andalso (Month@1 =< 12))
                            andalso (Day@1 >= 1))
                            andalso (Day@1 =< days_in_month(Year@1, Month@1))
                        end
                    )
                end
            )
        end
    ).

-file("src/molt/internal/classifier.gleam", 152).
?DOC(false).
-spec match_local_date(binary()) -> boolean().
match_local_date(Text) ->
    case gleam@string:split(Text, <<"-"/utf8>>) of
        [Year, Month, Day] ->
            valid_date_parts(Year, Month, Day);

        _ ->
            false
    end.

-file("src/molt/internal/classifier.gleam", 137).
?DOC(false).
-spec match_local_datetime(binary()) -> boolean().
match_local_datetime(Text) ->
    case molt@internal@utils:split_at(Text, 10) of
        {_, <<""/utf8>>} ->
            false;

        {Date_part, Rest} ->
            case {match_local_date(Date_part), Rest} of
                {true, <<"T"/utf8, Time_part/binary>>} ->
                    match_local_time(Time_part);

                {true, <<"t"/utf8, Time_part/binary>>} ->
                    match_local_time(Time_part);

                {true, <<" "/utf8, Time_part/binary>>} ->
                    match_local_time(Time_part);

                {_, _} ->
                    false
            end
    end.

-file("src/molt/internal/classifier.gleam", 340).
?DOC(false).
-spec match_offset_body(binary()) -> boolean().
match_offset_body(Text) ->
    case gleam@string:split(Text, <<":"/utf8>>) of
        [Hour, Minute] ->
            match_time_parts(Hour, Minute, <<""/utf8>>, <<""/utf8>>);

        _ ->
            false
    end.

-file("src/molt/internal/classifier.gleam", 321).
?DOC(false).
-spec do_match_time_offset(binary(), list(binary()), integer()) -> boolean().
do_match_time_offset(Input, Acc, Count) ->
    case Input of
        <<""/utf8>> ->
            false;

        <<"Z"/utf8, Input@1/binary>> ->
            (Input@1 =:= <<""/utf8>>) andalso match_local_time(
                molt@internal@utils:reverse_concat(Acc)
            );

        <<"z"/utf8, Input@1/binary>> ->
            (Input@1 =:= <<""/utf8>>) andalso match_local_time(
                molt@internal@utils:reverse_concat(Acc)
            );

        <<"+"/utf8, Input@2/binary>> when Count >= 5 ->
            match_local_time(molt@internal@utils:reverse_concat(Acc)) andalso match_offset_body(
                Input@2
            );

        <<"-"/utf8, Input@2/binary>> when Count >= 5 ->
            match_local_time(molt@internal@utils:reverse_concat(Acc)) andalso match_offset_body(
                Input@2
            );

        _ ->
            {Ch, Input@3} = molt@internal@utils:split_at(Input, 1),
            do_match_time_offset(Input@3, [Ch | Acc], Count + 1)
    end.

-file("src/molt/internal/classifier.gleam", 317).
?DOC(false).
-spec match_time_offset(binary()) -> boolean().
match_time_offset(Input) ->
    do_match_time_offset(Input, [], 0).

-file("src/molt/internal/classifier.gleam", 120).
?DOC(false).
-spec match_offset_datetime(binary()) -> boolean().
match_offset_datetime(Text) ->
    case molt@internal@utils:split_at(Text, 10) of
        {_, <<""/utf8>>} ->
            false;

        {Date_part, Rest} ->
            case {match_local_date(Date_part), Rest} of
                {true, <<"T"/utf8, Time_and_offset/binary>>} ->
                    match_time_offset(Time_and_offset);

                {true, <<"t"/utf8, Time_and_offset/binary>>} ->
                    match_time_offset(Time_and_offset);

                {true, <<" "/utf8, Time_and_offset/binary>>} ->
                    match_time_offset(Time_and_offset);

                {_, _} ->
                    false
            end
    end.

-file("src/molt/internal/classifier.gleam", 34).
?DOC(false).
-spec match_datetime(binary()) -> gleam@option:option(molt@types:toml_kind()).
match_datetime(Text) ->
    gleam@bool:guard(
        match_offset_datetime(Text),
        {some, offset_date_time},
        fun() ->
            gleam@bool:guard(
                match_local_datetime(Text),
                {some, local_date_time},
                fun() ->
                    gleam@bool:guard(
                        match_local_date(Text),
                        {some, local_date},
                        fun() ->
                            gleam@bool:guard(
                                match_local_time(Text),
                                {some, local_time},
                                fun() -> none end
                            )
                        end
                    )
                end
            )
        end
    ).

-file("src/molt/internal/classifier.gleam", 15).
?DOC(false).
-spec match_type(binary()) -> gleam@option:option(molt@types:toml_kind()).
match_type(Text) ->
    case Text of
        <<"true"/utf8>> ->
            {some, bool_true};

        <<"false"/utf8>> ->
            {some, bool_false};

        <<"inf"/utf8>> ->
            {some, inf};

        <<"+inf"/utf8>> ->
            {some, pos_inf};

        <<"-inf"/utf8>> ->
            {some, neg_inf};

        <<"nan"/utf8>> ->
            {some, na_n};

        <<"+nan"/utf8>> ->
            {some, pos_na_n};

        <<"-nan"/utf8>> ->
            {some, neg_na_n};

        _ ->
            _pipe = match_datetime(Text),
            gleam@option:lazy_or(_pipe, fun() -> match_number(Text) end)
    end.