1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
//! A performant rust implementation of recurrence rules as defined in the [iCalendar RFC](https://datatracker.ietf.org/doc/html/rfc5545).
//!
//! This crate provides [`RRuleSet`] for working with recurrence rules. It has a collection of `DTSTART`, `RRULE`s, `EXRULE`s, `RDATE`s and `EXDATE`s. Both the `RRULE` and `EXRULE`
//! properties are represented by the [`RRule`] type and the `DTSTART`, `RDATE` and `EXDATE` properties are represented by the [`chrono::DateTime<Tz>`].
//!
//! # Building `RRule` and `RRuleSet`
//! [`RRuleSet`] and [`RRule`] both implements the [`std::str::FromStr`] trait so that it can be parsed and built from a string representation.
//! [`RRuleSet`] can also be built by composing multiple [`RRule`]s for its `rrule` and `exrule` properties and [`chrono::DateTime<Tz>`] for its
//! `dt_start`, `exdate` and `rdate` properties. See the examples below.
//!
//! ```rust
//! use chrono::{DateTime, TimeZone};
//! use rrule::{RRuleSet, RRule, Tz, Unvalidated, Frequency};
//!
//! // Parse a single RRule string. Useful when you don't have a start date yet or
//! // just want to check if the input string is grammatically correct.
//! let rrule: RRule<Unvalidated> = "FREQ=DAILY;COUNT=40;INTERVAL=3".parse().unwrap();
//! assert_eq!(rrule.get_freq(), Frequency::Daily);
//! assert_eq!(rrule.get_count(), Some(40));
//! assert_eq!(rrule.get_interval(), 3);
//!
//! // Parse a RRuleSet string
//! let rrule_set: RRuleSet = "DTSTART:20120201T023000Z\n\
//! RRULE:FREQ=MONTHLY;COUNT=5\n\
//! RDATE:20120701T023000Z,20120702T023000Z\n\
//! EXDATE:20120601T023000Z".parse().unwrap();
//!
//! assert_eq!(*rrule_set.get_dt_start(), Tz::UTC.with_ymd_and_hms(2012, 2, 1,2, 30, 0).unwrap());
//! assert_eq!(rrule_set.get_rrule().len(), 1);
//! assert_eq!(rrule_set.get_rdate().len(), 2);
//! assert_eq!(rrule_set.get_exdate().len(), 1);
//!
//! // Add an rrule manually
//! let rrule = rrule.validate(*rrule_set.get_dt_start()).unwrap();
//! let rrule_set = rrule_set.rrule(rrule);
//! assert_eq!(rrule_set.get_rrule().len(), 2);
//! ```
//!
//! # Generating occurrences
//! You can loop over the occurrences of a [`RRuleSet`] by calling any of the following methods:
//! - [`RRuleSet::all`]: Generate all recurrences that match the rules (with a limit to prevent infinite loops).
//! - [`RRuleSet::all_unchecked`]: Generate all recurrences that match the rules (without a limit).
//! - ...
//!
//! If you have some additional filters or want to work with infinite recurrence rules
//! [`RRuleSet`] implements the `IntoIterator` trait which allows for very flexible queries.
//! All the methods above use the iterator trait in its implementation as shown below.
//! ```rust
//! use chrono::{DateTime, TimeZone};
//! use rrule::{RRuleSet, Tz};
//!
//! let rrule: RRuleSet = "DTSTART:20120201T093000Z\nRRULE:FREQ=DAILY;COUNT=3".parse().unwrap();
//! let result = rrule.all(100);
//!
//! // All dates
//! assert_eq!(
//! vec![
//! DateTime::parse_from_rfc3339("2012-02-01T09:30:00+00:00").unwrap(),
//! DateTime::parse_from_rfc3339("2012-02-02T09:30:00+00:00").unwrap(),
//! DateTime::parse_from_rfc3339("2012-02-03T09:30:00+00:00").unwrap(),
//! ],
//! result.dates
//! );
//! ```
//! Find all events that are within a given range.
//! ```rust
//! use chrono::{DateTime, TimeZone};
//! use rrule::{RRuleSet, Tz};
//!
//! let rrule: RRuleSet = "DTSTART:20120201T093000Z\nRRULE:FREQ=DAILY;COUNT=3".parse().unwrap();
//!
//! // Between two dates
//! let after = Tz::UTC.with_ymd_and_hms(2012, 2, 1,10, 0, 0).unwrap();
//! let before = Tz::UTC.with_ymd_and_hms(2012, 4, 1,9, 0, 0).unwrap();
//!
//! let rrule = rrule.after(after).before(before);
//! let result = rrule.all(100);
//!
//! assert_eq!(
//! vec![
//! DateTime::parse_from_rfc3339("2012-02-02T09:30:00+00:00").unwrap(),
//! DateTime::parse_from_rfc3339("2012-02-03T09:30:00+00:00").unwrap(),
//! ],
//! result.dates
//! );
//! ```
//!
//! Note: All the generated recurrence will be in the same time zone as the `dt_start` property.
//!
#![forbid(unsafe_code)]
#![deny(clippy::all)]
#![warn(missing_docs)]
#![deny(rustdoc::broken_intra_doc_links)]
mod core;
mod error;
mod iter;
mod parser;
mod tests;
mod validator;
pub use crate::core::{Frequency, NWeekday, RRule, RRuleResult, RRuleSet, Tz};
pub use crate::core::{Unvalidated, Validated};
pub use chrono::Weekday;
pub use error::{ParseError, RRuleError, ValidationError};
pub use iter::RRuleSetIter;