-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.