src/automata@rrule.erl

-module(automata@rrule).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/automata/rrule.gleam").
-export([parse/1, validate/1, normalize/2, to_string/1, matches_plan/2, matches/3, iterator_after_plan/2, iterator_after/3, next_after_plan/2, next_after/3, builder/1, weekday/1, nth_weekday/2, with_interval/2, with_count/2, with_until_date/2, with_until_datetime/2, without_end_condition/1, with_by_day/2, with_by_month/2, with_by_month_day/2, with_by_hour/2, with_by_minute/2, build/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(
    " RFC 5545 RRULE stack for the supported subset (FREQ, INTERVAL,\n"
    " COUNT, UNTIL, BYDAY, BYMONTH, BYMONTHDAY, BYHOUR, BYMINUTE).\n"
    " Anchored at a `ValidDateTime`, an RRULE expands into a concrete\n"
    " `RRulePlan` that the matcher and iterator consume. This module is\n"
    " the user-facing facade; the typed phases live in the\n"
    " `automata/rrule/{ast,parser,validator,normalize,evaluator,iterator,\n"
    " next,builder}` submodules.\n"
).

-file("src/automata/rrule.gleam", 25).
?DOC(
    " Parse an RFC 5545 RRULE string into a `RawRRule` AST. Returns a\n"
    " `ParseError` for syntactic mistakes; semantic validation (FREQ in\n"
    " range, UNTIL/COUNT mutual exclusion, etc.) happens in `validate/1`.\n"
).
-spec parse(binary()) -> {ok, automata@rrule@ast:raw_r_rule()} |
    {error, automata@rrule@parser:parse_error()}.
parse(Input) ->
    automata@rrule@parser:parse(Input).

-file("src/automata/rrule.gleam", 34).
?DOC(
    " Validate a `RawRRule` and return a `ValidRRule` (opaque) covering\n"
    " the supported subset. Anchorless: anchor-dependent expansion is\n"
    " done by `normalize/2`.\n"
).
-spec validate(automata@rrule@ast:raw_r_rule()) -> {ok,
        automata@rrule@validator:valid_r_rule()} |
    {error, automata@rrule@validator:validation_error()}.
validate(Raw) ->
    automata@rrule@validator:validate(Raw).

-file("src/automata/rrule.gleam", 44).
?DOC(
    " Expand a `ValidRRule` against an anchor `ValidDateTime`, producing\n"
    " an `RRulePlan` ready for matching/iteration. Reuse the plan across\n"
    " calls if you evaluate the same spec/anchor pair many times — see\n"
    " the `*_plan` variants.\n"
).
-spec normalize(
    automata@rrule@validator:valid_r_rule(),
    automata@schedule@ast:valid_date_time()
) -> {ok, automata@rrule@normalize:r_rule_plan()} |
    {error, automata@rrule@normalize:normalize_error()}.
normalize(Spec, Anchor) ->
    automata@rrule@normalize:normalize(
        Spec,
        automata@schedule@ast:valid_datetime_value(Anchor)
    ).

-file("src/automata/rrule.gleam", 55).
?DOC(" Render a `ValidRRule` back to its canonical RRULE string form.\n").
-spec to_string(automata@rrule@validator:valid_r_rule()) -> binary().
to_string(Spec) ->
    automata@rrule@validator:to_string(Spec).

-file("src/automata/rrule.gleam", 110).
?DOC(
    " Plan-reuse counterpart to `matches/3`. Takes an already-normalised\n"
    " `RRulePlan` so callers that evaluate the same spec/anchor pair many\n"
    " times can pay the normalisation cost once.\n"
).
-spec matches_plan(
    automata@rrule@normalize:r_rule_plan(),
    automata@schedule@ast:valid_date_time()
) -> boolean().
matches_plan(Plan, At) ->
    automata@rrule@evaluator:matches(
        Plan,
        automata@schedule@ast:valid_datetime_value(At)
    ).

-file("src/automata/rrule.gleam", 59).
-spec matches(
    automata@rrule@validator:valid_r_rule(),
    automata@schedule@ast:valid_date_time(),
    automata@schedule@ast:valid_date_time()
) -> {ok, boolean()} | {error, automata@rrule@normalize:normalize_error()}.
matches(Spec, Anchor, At) ->
    case automata@rrule@normalize:normalize(
        Spec,
        automata@schedule@ast:valid_datetime_value(Anchor)
    ) of
        {ok, Plan} ->
            {ok, matches_plan(Plan, At)};

        {error, Error} ->
            {error, Error}
    end.

-file("src/automata/rrule.gleam", 118).
?DOC(" Plan-reuse counterpart to `iterator_after/3`.\n").
-spec iterator_after_plan(
    automata@rrule@normalize:r_rule_plan(),
    automata@schedule@ast:boundary()
) -> automata@rrule@iterator:r_rule_iterator().
iterator_after_plan(Plan, Boundary) ->
    automata@rrule@iterator:'after'(Plan, Boundary).

-file("src/automata/rrule.gleam", 75).
-spec iterator_after(
    automata@rrule@validator:valid_r_rule(),
    automata@schedule@ast:valid_date_time(),
    automata@schedule@ast:boundary()
) -> {ok, automata@rrule@iterator:r_rule_iterator()} |
    {error, automata@rrule@normalize:normalize_error()}.
iterator_after(Spec, Anchor, Boundary) ->
    case automata@rrule@normalize:normalize(
        Spec,
        automata@schedule@ast:valid_datetime_value(Anchor)
    ) of
        {ok, Plan} ->
            {ok, iterator_after_plan(Plan, Boundary)};

        {error, Error} ->
            {error, Error}
    end.

-file("src/automata/rrule.gleam", 126).
?DOC(" Plan-reuse counterpart to `next_after/3`.\n").
-spec next_after_plan(
    automata@rrule@normalize:r_rule_plan(),
    automata@schedule@ast:valid_date_time()
) -> gleam@option:option(automata@schedule@ast:valid_date_time()).
next_after_plan(Plan, After) ->
    _pipe = automata@rrule@next:next_after(
        Plan,
        automata@schedule@ast:valid_datetime_value(After)
    ),
    gleam@option:map(_pipe, fun automata@schedule@ast:unsafe_assume_valid/1).

-file("src/automata/rrule.gleam", 91).
-spec next_after(
    automata@rrule@validator:valid_r_rule(),
    automata@schedule@ast:valid_date_time(),
    automata@schedule@ast:valid_date_time()
) -> {ok, gleam@option:option(automata@schedule@ast:valid_date_time())} |
    {error, automata@rrule@normalize:normalize_error()}.
next_after(Spec, Anchor, After) ->
    case automata@rrule@normalize:normalize(
        Spec,
        automata@schedule@ast:valid_datetime_value(Anchor)
    ) of
        {ok, Plan} ->
            {ok, next_after_plan(Plan, After)};

        {error, Error} ->
            {error, Error}
    end.

-file("src/automata/rrule.gleam", 134).
-spec builder(automata@rrule@validator:frequency()) -> automata@rrule@builder:builder().
builder(Frequency) ->
    automata@rrule@builder:builder(Frequency).

-file("src/automata/rrule.gleam", 138).
-spec weekday(automata@schedule@ast:weekday()) -> automata@rrule@validator:weekday_specifier().
weekday(Day) ->
    automata@rrule@builder:weekday(Day).

-file("src/automata/rrule.gleam", 142).
-spec nth_weekday(integer(), automata@schedule@ast:weekday()) -> automata@rrule@validator:weekday_specifier().
nth_weekday(Ordinal, Day) ->
    automata@rrule@builder:nth_weekday(Ordinal, Day).

-file("src/automata/rrule.gleam", 149).
-spec with_interval(automata@rrule@builder:builder(), integer()) -> automata@rrule@builder:builder().
with_interval(Builder, Interval) ->
    automata@rrule@builder:with_interval(Builder, Interval).

-file("src/automata/rrule.gleam", 156).
-spec with_count(automata@rrule@builder:builder(), integer()) -> automata@rrule@builder:builder().
with_count(Builder, Count) ->
    automata@rrule@builder:with_count(Builder, Count).

-file("src/automata/rrule.gleam", 163).
-spec with_until_date(
    automata@rrule@builder:builder(),
    automata@schedule@ast:date()
) -> automata@rrule@builder:builder().
with_until_date(Builder, Until) ->
    automata@rrule@builder:with_until_date(Builder, Until).

-file("src/automata/rrule.gleam", 170).
-spec with_until_datetime(
    automata@rrule@builder:builder(),
    automata@schedule@ast:valid_date_time()
) -> automata@rrule@builder:builder().
with_until_datetime(Builder, Until) ->
    automata@rrule@builder:with_until_datetime(
        Builder,
        automata@schedule@ast:valid_datetime_value(Until)
    ).

-file("src/automata/rrule.gleam", 180).
-spec without_end_condition(automata@rrule@builder:builder()) -> automata@rrule@builder:builder().
without_end_condition(Builder) ->
    automata@rrule@builder:without_end_condition(Builder).

-file("src/automata/rrule.gleam", 186).
-spec with_by_day(
    automata@rrule@builder:builder(),
    list(automata@rrule@validator:weekday_specifier())
) -> automata@rrule@builder:builder().
with_by_day(Builder, Values) ->
    automata@rrule@builder:with_by_day(Builder, Values).

-file("src/automata/rrule.gleam", 193).
-spec with_by_month(automata@rrule@builder:builder(), list(integer())) -> automata@rrule@builder:builder().
with_by_month(Builder, Values) ->
    automata@rrule@builder:with_by_month(Builder, Values).

-file("src/automata/rrule.gleam", 200).
-spec with_by_month_day(automata@rrule@builder:builder(), list(integer())) -> automata@rrule@builder:builder().
with_by_month_day(Builder, Values) ->
    automata@rrule@builder:with_by_month_day(Builder, Values).

-file("src/automata/rrule.gleam", 207).
-spec with_by_hour(automata@rrule@builder:builder(), list(integer())) -> automata@rrule@builder:builder().
with_by_hour(Builder, Values) ->
    automata@rrule@builder:with_by_hour(Builder, Values).

-file("src/automata/rrule.gleam", 214).
-spec with_by_minute(automata@rrule@builder:builder(), list(integer())) -> automata@rrule@builder:builder().
with_by_minute(Builder, Values) ->
    automata@rrule@builder:with_by_minute(Builder, Values).

-file("src/automata/rrule.gleam", 221).
-spec build(automata@rrule@builder:builder()) -> {ok,
        automata@rrule@validator:valid_r_rule()} |
    {error, automata@rrule@validator:validation_error()}.
build(Builder) ->
    automata@rrule@builder:build(Builder).