summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFredrik Meringdal <fmeringdal@hotmail.com>2020-10-31 18:15:21 +0100
committerFredrik Meringdal <fmeringdal@hotmail.com>2020-10-31 18:15:21 +0100
commita600a4e88053a89670aa4bdec0800089018f5c1c (patch)
tree8911ff0dbb9ba184573630aca58a241157a4234a
parent93a7edadb0b920752d17c170aa02eda1d4953134 (diff)
downloadrust_rrule-a600a4e88053a89670aa4bdec0800089018f5c1c.zip
removed more unwraps
-rw-r--r--src/datetime.rs2
-rw-r--r--src/iter/iterinfo.rs8
-rw-r--r--src/iter/mod.rs44
-rw-r--r--src/iter/monthinfo.rs2
-rw-r--r--src/iter/poslist.rs11
-rw-r--r--src/iter/utils.rs2
-rw-r--r--src/iter/yearinfo.rs5
-rw-r--r--src/lib.rs54
-rw-r--r--src/options.rs10
-rw-r--r--src/parse_options.rs140
-rw-r--r--src/rrule.rs6
-rw-r--r--src/rruleset.rs15
-rw-r--r--src/rruleset_iter.rs12
-rw-r--r--src/rrulestr.rs296
-rw-r--r--tests/rrule.rs4
15 files changed, 338 insertions, 273 deletions
diff --git a/src/datetime.rs b/src/datetime.rs
index 46b1643..4702e0e 100644
--- a/src/datetime.rs
+++ b/src/datetime.rs
@@ -1,7 +1,6 @@
use chrono::prelude::*;
use chrono_tz::Tz;
-
pub type DTime = DateTime<Tz>;
// pub fn from_ordinal(ordinal: isize) -> DateTime<Utc> {
@@ -15,7 +14,6 @@ pub fn from_ordinal(ordinal: isize, tz: &Tz) -> DTime {
tz.timestamp(timestamp as i64, 0)
}
-
pub fn to_ordinal(date: &DateTime<Utc>) -> isize {
(date.timestamp() / 60 / 60 / 24) as isize
}
diff --git a/src/iter/iterinfo.rs b/src/iter/iterinfo.rs
index 1d401dd..7c47645 100644
--- a/src/iter/iterinfo.rs
+++ b/src/iter/iterinfo.rs
@@ -1,8 +1,8 @@
-use crate::datetime::{Time, to_ordinal};
+use crate::datetime::{to_ordinal, Time};
use crate::iter::easter::easter;
-use crate::iter::monthinfo::{MonthInfo, rebuild_month};
-use crate::iter::yearinfo::{YearInfo, rebuild_year};
-use crate::options::{ParsedOptions, Frequenzy};
+use crate::iter::monthinfo::{rebuild_month, MonthInfo};
+use crate::iter::yearinfo::{rebuild_year, YearInfo};
+use crate::options::{Frequenzy, ParsedOptions};
use chrono::prelude::*;
pub struct IterInfo<'a> {
diff --git a/src/iter/mod.rs b/src/iter/mod.rs
index 8e0d4a5..790ef67 100644
--- a/src/iter/mod.rs
+++ b/src/iter/mod.rs
@@ -1,6 +1,6 @@
-mod yearinfo;
-mod monthinfo;
mod iterinfo;
+mod monthinfo;
+mod yearinfo;
use iterinfo::IterInfo;
mod poslist;
use poslist::build_poslist;
@@ -8,8 +8,8 @@ mod easter;
mod masks;
mod utils;
+use crate::datetime::{from_ordinal, get_weekday_val, DTime, Time};
use crate::options::*;
-use crate::datetime::{Time, DTime, from_ordinal, get_weekday_val};
use chrono::offset::TimeZone;
use chrono::prelude::*;
use chrono::Duration;
@@ -19,11 +19,7 @@ pub trait IterResult {
fn get_value(&self) -> Vec<DTime>;
}
-pub fn iter<T: IterResult>(
- iter_result: &mut T,
- options: &mut ParsedOptions,
-) -> Vec<DTime> {
-
+pub fn iter<T: IterResult>(iter_result: &mut T, options: &mut ParsedOptions) -> Vec<DTime> {
if (options.count.is_some() && options.count.unwrap() == 0) || options.interval == 0 {
return iter_result.get_value();
}
@@ -54,7 +50,15 @@ pub fn iter<T: IterResult>(
let filtered = remove_filtered_days(&mut dayset, start, end, &ii, options);
if not_empty(&options.bysetpos) {
- let poslist = build_poslist(&options.bysetpos, &timeset, start, end, &ii, &dayset, &options.tzid);
+ let poslist = build_poslist(
+ &options.bysetpos,
+ &timeset,
+ start,
+ end,
+ &ii,
+ &dayset,
+ &options.tzid,
+ );
for j in 0..poslist.len() {
let res = poslist[j];
@@ -85,11 +89,14 @@ pub fn iter<T: IterResult>(
let current_day = current_day.unwrap();
let date = from_ordinal(ii.yearordinal().unwrap() + current_day, &options.tzid);
for k in 0..timeset.len() {
- let res = options.tzid.ymd(date.year(), date.month(), date.day()).and_hms(
- timeset[k].hour as u32,
- timeset[k].minute as u32,
- timeset[k].second as u32,
- );
+ let res = options
+ .tzid
+ .ymd(date.year(), date.month(), date.day())
+ .and_hms(
+ timeset[k].hour as u32,
+ timeset[k].minute as u32,
+ timeset[k].second as u32,
+ );
if options.until.is_some() && res > options.until.unwrap() {
return iter_result.get_value();
}
@@ -97,7 +104,7 @@ pub fn iter<T: IterResult>(
if !iter_result.accept(res) {
return iter_result.get_value();
}
-
+
if count > 0 {
count -= 1;
if count == 0 {
@@ -137,7 +144,6 @@ pub fn iter<T: IterResult>(
}
}
-
pub fn increment_counter_date(
counter_date: DTime,
options: &ParsedOptions,
@@ -289,11 +295,7 @@ pub fn build_timeset(options: &ParsedOptions) -> Vec<Time> {
timeset
}
-pub fn make_timeset(
- ii: &IterInfo,
- counter_date: &DTime,
- options: &ParsedOptions,
-) -> Vec<Time> {
+pub fn make_timeset(ii: &IterInfo, counter_date: &DTime, options: &ParsedOptions) -> Vec<Time> {
if options.freq < Frequenzy::Hourly {
return build_timeset(options);
}
diff --git a/src/iter/monthinfo.rs b/src/iter/monthinfo.rs
index 9da2ca6..43fcef8 100644
--- a/src/iter/monthinfo.rs
+++ b/src/iter/monthinfo.rs
@@ -1,5 +1,5 @@
-use crate::options::*;
use crate::iter::utils::pymod;
+use crate::options::*;
#[derive(Debug)]
pub struct MonthInfo {
diff --git a/src/iter/poslist.rs b/src/iter/poslist.rs
index fd564d4..9166073 100644
--- a/src/iter/poslist.rs
+++ b/src/iter/poslist.rs
@@ -1,7 +1,7 @@
-use crate::datetime::{Time, DTime};
+use crate::datetime::from_ordinal;
+use crate::datetime::{DTime, Time};
use crate::iter::iterinfo::IterInfo;
use crate::iter::utils::pymod;
-use crate::datetime::from_ordinal;
use chrono::prelude::*;
use chrono_tz::Tz;
@@ -12,7 +12,7 @@ pub fn build_poslist(
end: usize,
ii: &IterInfo,
dayset: &Vec<Option<isize>>,
- tz: &Tz
+ tz: &Tz,
) -> Vec<DTime> {
let mut poslist = vec![];
@@ -31,8 +31,9 @@ pub fn build_poslist(
let mut tmp = vec![];
for k in start..end {
let val = dayset[k];
- if val.is_some() {
- tmp.push(val.unwrap());
+ match val {
+ Some(v) => tmp.push(v),
+ None => (),
}
}
diff --git a/src/iter/utils.rs b/src/iter/utils.rs
index c168c92..a134d9a 100644
--- a/src/iter/utils.rs
+++ b/src/iter/utils.rs
@@ -5,4 +5,4 @@ pub fn pymod(a: isize, b: isize) -> isize {
return r + b;
}
r
-} \ No newline at end of file
+}
diff --git a/src/iter/yearinfo.rs b/src/iter/yearinfo.rs
index 40b48ce..308a205 100644
--- a/src/iter/yearinfo.rs
+++ b/src/iter/yearinfo.rs
@@ -1,9 +1,8 @@
+use crate::datetime::{get_weekday_val, get_year_len, to_ordinal};
use crate::iter::masks::MASKS;
+use crate::iter::utils::pymod;
use crate::options::*;
use chrono::prelude::*;
-use crate::datetime::{to_ordinal, get_year_len, get_weekday_val};
-use crate::iter::utils::pymod;
-
#[derive(Debug)]
pub struct YearInfo {
diff --git a/src/lib.rs b/src/lib.rs
index 8b9db67..4f880d6 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -22,7 +22,7 @@
//! extern crate rrule;
//!
//! use rrule::build_rruleset;
-//!
+//!
//! // Parse a RRuleSet string, return a RRuleSet type
//! let mut rrule = build_rruleset("DTSTART:20120201T023000Z\nRRULE:FREQ=MONTHLY;COUNT=5\nRDATE:20120701T023000Z,20120702T023000Z\nEXRULE:FREQ=MONTHLY;COUNT=2\nEXDATE:20120601T023000Z").unwrap();
//! assert_eq!(rrule.all().len(), 4);
@@ -34,13 +34,13 @@
//!
//! ```
//! extern crate rrule;
-//! extern crate chrono;
-//! extern crate chrono_tz;
+//! extern crate chrono;
+//! extern crate chrono_tz;
//!
//! use chrono::prelude::*;
//! use chrono_tz::UTC;
//! use rrule::{RRule, RRuleSet, Options, Frequenzy, Weekday};
-//!
+//!
//! // Build options that starts first day in 2020 at 9:00AM and occurs daily 5 times
//! let mut options = Options::new()
//! .dtstart(UTC.ymd(2020, 1, 1).and_hms(9, 0, 0))
@@ -48,8 +48,8 @@
//! .freq(Frequenzy::Daily)
//! .build()
//! .unwrap();
-//!
-//! // Construct `RRule` from options
+//!
+//! // Construct `RRule` from options
//! let mut rrule = RRule::new(options);
//! let occurences = rrule.all();
//! for i in 0..5 {
@@ -78,8 +78,8 @@
//! .byweekday(vec![Weekday::Tue, Weekday::Wed])
//! .build()
//! .unwrap();
-//!
-//! // Construct `RRule` from options
+//!
+//! // Construct `RRule` from options
//! let mut rrule = RRule::new(rrule_options);
//!
//!
@@ -91,21 +91,21 @@
//! .byweekday(vec![Weekday::Wed])
//! .build()
//! .unwrap();
-//!
-//! // Construct `RRule` from options
+//!
+//! // Construct `RRule` from options
//! let mut exrule = RRule::new(exrule_options);
//!
//! // Now create the RRuleSet and add rrule and exrule
//! let mut rrule_set = RRuleSet::new();
//! rrule_set.rrule(rrule);
//! rrule_set.exrule(exrule);
-//!
+//!
//! let occurences = rrule_set.all();
//!
//! for occurence in &occurences {
//! assert_eq!(occurence.weekday(), Weekday::Tue);
//! }
-//!
+//!
//! assert_eq!(occurences.len(), 2);
//! ```
//!
@@ -116,8 +116,8 @@
//!
//! ```
//! extern crate rrule;
-//! extern crate chrono;
-//! extern crate chrono_tz;
+//! extern crate chrono;
+//! extern crate chrono_tz;
//!
//! use chrono::prelude::*;
//! use chrono_tz::{UTC, Tz};
@@ -125,13 +125,13 @@
//! use rrule::{RRule, RRuleSet, Options, Frequenzy, Weekday};
//!
//! // SOME NOTES:
-//! // Occurences produces by RRule or RRuleSet will be in the same timezone
-//! // as the start datetime provided (dtstart). The `until` datetime MUST
+//! // Occurences produces by RRule or RRuleSet will be in the same timezone
+//! // as the start datetime provided (dtstart). The `until` datetime MUST
//! // always be specified with the UTC timezone if it is specified.
//! // Example:
-//! // The following examples uses an RRuleSet with an RRule that yields occurences
-//! // in the Europe/Berlin timezone, and contains one EXDATE that is specified
+//! // The following examples uses an RRuleSet with an RRule that yields occurences
+//! // in the Europe/Berlin timezone, and contains one EXDATE that is specified
//! // in UTC and collides (and therefore filters away) with one of those occurences.
//!
//!
@@ -142,25 +142,25 @@
//! .freq(Frequenzy::Daily)
//! .build()
//! .unwrap();
-//!
+//!
//! let mut rrule = RRule::new(rrule_options);
//!
-//! // Exdate in the UTC at 8 oclock which is 9 oclock in Berlin and therefore
+//! // Exdate in the UTC at 8 oclock which is 9 oclock in Berlin and therefore
//! // collides with one of the rrule occurences.
//! let exdate = UTC.ymd(2020, 1, 2).and_hms(8, 0, 0);
-//!
+//!
//! // Now create the RRuleSet and add rrule and exdate
//! let mut rrule_set = RRuleSet::new();
//! rrule_set.rrule(rrule);
//! rrule_set.exdate(exdate);
-//!
+//!
//! let occurences = rrule_set.all();
//! // RRule contained 4 occurences but 1 was filtered away by the exdate
//! assert_eq!(occurences.len(), 3);
//!
//! // If you want to get back the DateTimes in another timezone (In this case Moscow).
//! // Refer to the chrono and chrono-tz crates for more documentation on how to work with
-//! // their DateTime type and timezones.
+//! // their DateTime type and timezones.
//! let occurences_in_moscow_tz: Vec<DateTime<Tz>> = occurences.iter()
//! .map(|d| d.with_timezone(&Moscow)).collect();
//! ```
@@ -172,16 +172,16 @@ extern crate regex;
mod datetime;
mod iter;
-mod parse_options;
mod options;
-mod rrulestr;
+mod parse_options;
mod rrule;
mod rrule_iter;
mod rruleset;
mod rruleset_iter;
+mod rrulestr;
+pub use crate::options::{Frequenzy, Options, ParsedOptions};
pub use crate::rrule::RRule;
pub use crate::rruleset::RRuleSet;
pub use crate::rrulestr::{build_rrule, build_rruleset};
-pub use crate::options::{Frequenzy, ParsedOptions, Options};
-pub use chrono::Weekday; \ No newline at end of file
+pub use chrono::Weekday;
diff --git a/src/options.rs b/src/options.rs
index 7b5c7e3..884ecb6 100644
--- a/src/options.rs
+++ b/src/options.rs
@@ -1,7 +1,7 @@
+use crate::datetime::{get_weekday_val, DTime};
+use crate::parse_options::parse_options;
use chrono::prelude::*;
use chrono_tz::Tz;
-use crate::datetime::{DTime, get_weekday_val};
-use crate::parse_options::parse_options;
use std::error::Error;
use std::fmt::{Display, Formatter};
@@ -92,7 +92,7 @@ impl Options {
fn is_some_or_none<'a, T>(prop1: &'a Option<T>, prop2: &'a Option<T>) -> &'a Option<T> {
if prop2.is_some() {
return prop2;
- }
+ }
prop1
}
@@ -177,7 +177,7 @@ impl Options {
}
pub fn byweekday(mut self, byweekday: Vec<Weekday>) -> Self {
- let byweekday = byweekday.iter().map(|w| get_weekday_val(w)).collect();
+ let byweekday = byweekday.iter().map(|w| get_weekday_val(w)).collect();
self.byweekday = Some(byweekday);
self
}
@@ -216,4 +216,4 @@ impl Display for RRuleParseError {
}
}
-impl Error for RRuleParseError{} \ No newline at end of file
+impl Error for RRuleParseError {}
diff --git a/src/parse_options.rs b/src/parse_options.rs
index ef9efb7..727da86 100644
--- a/src/parse_options.rs
+++ b/src/parse_options.rs
@@ -1,5 +1,5 @@
+use crate::options::{Frequenzy, Options, ParsedOptions, RRuleParseError};
use chrono::prelude::*;
-use crate::options::{ParsedOptions, Frequenzy, Options, RRuleParseError};
use chrono_tz::{Tz, UTC};
// TODO: Validation
@@ -8,14 +8,13 @@ pub fn parse_options(options: &Options) -> Result<ParsedOptions, RRuleParseError
default_partial_options.interval = Some(1);
default_partial_options.freq = Some(Frequenzy::Yearly);
default_partial_options.wkst = Some(0);
-
+
let tzid: Tz = if options.tzid.is_some() {
options.tzid.clone().unwrap()
} else {
UTC
};
-
let mut partial_options = Options::concat(&default_partial_options, options);
if partial_options.byeaster.is_some() {
@@ -34,60 +33,61 @@ pub fn parse_options(options: &Options) -> Result<ParsedOptions, RRuleParseError
if let Some(bysetpos) = &partial_options.bysetpos {
for pos in bysetpos {
if *pos == 0 || !(*pos >= -366 && *pos <= 366) {
- return Err(RRuleParseError(String::from("Bysetpos must be between 1 and 366, or between -366 and -1")));
+ return Err(RRuleParseError(String::from(
+ "Bysetpos must be between 1 and 366, or between -366 and -1",
+ )));
}
}
}
-
let dtstart = if partial_options.dtstart.is_some() {
partial_options.dtstart.unwrap()
} else {
return Err(RRuleParseError(String::from("Dtstart was not specified")));
};
- if !(
- partial_options.byweekno.is_some() ||
- is_some_and_not_empty(&partial_options.byweekno) ||
- is_some_and_not_empty(&partial_options.byyearday) ||
- partial_options.bymonthday.is_some() ||
- is_some_and_not_empty(&partial_options.bymonthday) ||
- partial_options.byweekday.is_some() ||
- partial_options.byeaster.is_some()
- ) {
+ if !(partial_options.byweekno.is_some()
+ || is_some_and_not_empty(&partial_options.byweekno)
+ || is_some_and_not_empty(&partial_options.byyearday)
+ || partial_options.bymonthday.is_some()
+ || is_some_and_not_empty(&partial_options.bymonthday)
+ || partial_options.byweekday.is_some()
+ || partial_options.byeaster.is_some())
+ {
match &freq {
Frequenzy::Yearly => {
if partial_options.bymonth.is_none() {
partial_options.bymonth = Some(vec![dtstart.month() as usize]);
}
partial_options.bymonthday = Some(vec![dtstart.day() as isize]);
- },
+ }
Frequenzy::Monthly => {
- partial_options.bymonthday = Some(vec![dtstart.day() as isize]);
- },
+ partial_options.bymonthday = Some(vec![dtstart.day() as isize]);
+ }
Frequenzy::Weekly => {
partial_options.byweekday = Some(vec![dtstart.weekday() as usize]);
- },
- _ => ()
+ }
+ _ => (),
};
}
- if partial_options.bymonthday.is_none() {
- partial_options.bynmonthday = None;
- } else {
- let mut bymonthday = vec![];
- let mut bynmonthday = vec![];
-
- for v in &partial_options.bymonthday.unwrap() {
- if *v > 0 {
- bymonthday.push(*v);
- } else if *v < 0 {
- bynmonthday.push(*v);
- }
- }
-
- partial_options.bymonthday = Some(bymonthday);
- partial_options.bynmonthday = Some(bynmonthday);
+ match &partial_options.bymonthday {
+ None => partial_options.bynmonthday = None,
+ Some(opts_bymonthday) => {
+ let mut bymonthday = vec![];
+ let mut bynmonthday = vec![];
+
+ for v in opts_bymonthday {
+ if *v > 0 {
+ bymonthday.push(*v);
+ } else if *v < 0 {
+ bynmonthday.push(*v);
+ }
+ }
+
+ partial_options.bymonthday = Some(bymonthday);
+ partial_options.bynmonthday = Some(bynmonthday);
+ }
}
// byweekday / bynweekday
@@ -95,49 +95,47 @@ pub fn parse_options(options: &Options) -> Result<ParsedOptions, RRuleParseError
partial_options.bynweekday = None;
}
- // byhour
- if partial_options.byhour.is_none() && freq < Frequenzy::Hourly {
+ // byhour
+ if partial_options.byhour.is_none() && freq < Frequenzy::Hourly {
partial_options.byhour = Some(vec![dtstart.hour() as usize]);
- }
+ }
- // byminute
- if partial_options.byminute.is_none() && freq < Frequenzy::Minutely {
- partial_options.byminute = Some(vec![dtstart.minute() as usize]);
-}
+ // byminute
+ if partial_options.byminute.is_none() && freq < Frequenzy::Minutely {
+ partial_options.byminute = Some(vec![dtstart.minute() as usize]);
+ }
+ // bysecond
+ if partial_options.bysecond.is_none() && freq < Frequenzy::Secondly {
+ partial_options.bysecond = Some(vec![dtstart.second() as usize]);
+ }
- // bysecond
- if partial_options.bysecond.is_none() && freq < Frequenzy::Secondly {
- partial_options.bysecond = Some(vec![dtstart.second() as usize]);
- }
-
-
- Ok(ParsedOptions {
- freq,
- interval: partial_options.interval.unwrap(),
- count: partial_options.count,
- until: partial_options.until,
- tzid,
- dtstart,
- wkst: partial_options.wkst.unwrap(),
- bysetpos: partial_options.bysetpos.unwrap_or(vec![]),
- bymonth: partial_options.bymonth.unwrap_or(vec![]),
- bymonthday: partial_options.bymonthday.unwrap_or(vec![]),
- bynmonthday: partial_options.bynmonthday.unwrap_or(vec![]),
- byyearday: partial_options.byyearday.unwrap_or(vec![]),
- byweekno: partial_options.byweekno.unwrap_or(vec![]),
- byweekday: partial_options.byweekday.unwrap_or(vec![]),
- byhour: partial_options.byhour.unwrap_or(vec![]),
- byminute: partial_options.byminute.unwrap_or(vec![]),
- bysecond: partial_options.bysecond.unwrap_or(vec![]),
- bynweekday: partial_options.bynweekday.unwrap_or(vec![]),
- byeaster: partial_options.byeaster,
- })
+ Ok(ParsedOptions {
+ freq,
+ interval: partial_options.interval.unwrap(),
+ count: partial_options.count,
+ until: partial_options.until,
+ tzid,
+ dtstart,
+ wkst: partial_options.wkst.unwrap(),
+ bysetpos: partial_options.bysetpos.unwrap_or(vec![]),
+ bymonth: partial_options.bymonth.unwrap_or(vec![]),
+ bymonthday: partial_options.bymonthday.unwrap_or(vec![]),
+ bynmonthday: partial_options.bynmonthday.unwrap_or(vec![]),
+ byyearday: partial_options.byyearday.unwrap_or(vec![]),
+ byweekno: partial_options.byweekno.unwrap_or(vec![]),
+ byweekday: partial_options.byweekday.unwrap_or(vec![]),
+ byhour: partial_options.byhour.unwrap_or(vec![]),
+ byminute: partial_options.byminute.unwrap_or(vec![]),
+ bysecond: partial_options.bysecond.unwrap_or(vec![]),
+ bynweekday: partial_options.bynweekday.unwrap_or(vec![]),
+ byeaster: partial_options.byeaster,
+ })
}
fn is_some_and_not_empty<T>(v: &Option<Vec<T>>) -> bool {
match v {
Some(v) => !v.is_empty(),
- None => false
+ None => false,
}
-} \ No newline at end of file
+}
diff --git a/src/rrule.rs b/src/rrule.rs
index d7aa198..5083161 100644
--- a/src/rrule.rs
+++ b/src/rrule.rs
@@ -1,6 +1,6 @@
use crate::iter::iter;
-use crate::rrule_iter::*;
use crate::options::*;
+use crate::rrule_iter::*;
use chrono::prelude::*;
use chrono_tz::Tz;
@@ -11,9 +11,7 @@ pub struct RRule {
impl RRule {
pub fn new(options: ParsedOptions) -> Self {
- Self {
- options
- }
+ Self { options }
}
pub fn all(&mut self) -> Vec<DateTime<Tz>> {
diff --git a/src/rruleset.rs b/src/rruleset.rs
index 810c18e..4a3caa5 100644
--- a/src/rruleset.rs
+++ b/src/rruleset.rs
@@ -1,8 +1,8 @@
-use chrono::prelude::*;
-use chrono_tz::{Tz, UTC};
-use crate::rrule::RRule;
use crate::datetime::DTime;
+use crate::rrule::RRule;
use crate::rruleset_iter::RRuleSetIter;
+use chrono::prelude::*;
+use chrono_tz::{Tz, UTC};
#[derive(Debug)]
pub struct RRuleSet {
@@ -81,14 +81,7 @@ mod test_iter_set {
use super::*;
use crate::options::*;
- fn ymd_hms(
- year: i32,
- month: u32,
- day: u32,
- hour: u32,
- minute: u32,
- second: u32,
- ) -> DTime {
+ fn ymd_hms(year: i32, month: u32, day: u32, hour: u32, minute: u32, second: u32) -> DTime {
UTC.ymd(year, month, day).and_hms(hour, minute, second)
}
diff --git a/src/rruleset_iter.rs b/src/rruleset_iter.rs
index 495bbf3..0b3fe35 100644
--- a/src/rruleset_iter.rs
+++ b/src/rruleset_iter.rs
@@ -1,9 +1,9 @@
use crate::iter::{iter, IterResult};
-use chrono::prelude::*;
-use crate::rruleset::RRuleSet;
use crate::rrule_iter::*;
-use std::collections::HashMap;
+use crate::rruleset::RRuleSet;
+use chrono::prelude::*;
use chrono_tz::Tz;
+use std::collections::HashMap;
/// Result iterator for the RRuleSet type. It mostly just wraps
/// `RRuleIterRes` and also before accepting any date makes sure that
@@ -99,7 +99,10 @@ impl<'a> RRuleSetIter<'a> {
fn accept_when_unknown_bounds(&mut self, date: DateTime<Tz>) -> bool {
let dt = date.timestamp();
if !self.exdate_hash.contains_key(&dt) {
- self.eval_exdate(&date.timezone().timestamp(dt - 1, 0), &date.timezone().timestamp(dt + 1, 0));
+ self.eval_exdate(
+ &date.timezone().timestamp(dt - 1, 0),
+ &date.timezone().timestamp(dt + 1, 0),
+ );
if !self.exdate_hash.contains_key(&dt) {
self.exdate_hash.insert(dt, ());
return self.iter_res.accept(date);
@@ -122,7 +125,6 @@ impl<'a> RRuleSetIter<'a> {
}
pub fn iter(&mut self) -> Vec<DateTime<Tz>> {
-
// Add all exdates to exdate_hash
for date in &self.rrule_set.exdate {
println!("Exdate timestamp: {}", date.timestamp());
diff --git a/src/rrulestr.rs b/src/rrulestr.rs
index 4fceee4..87fedaa 100644
--- a/src/rrulestr.rs
+++ b/src/rrulestr.rs
@@ -1,10 +1,10 @@
+use crate::datetime::DTime;
use crate::options::*;
use crate::parse_options::parse_options;
use crate::rrule::RRule;
-use crate::datetime::DTime;
use crate::rruleset::RRuleSet;
use chrono::prelude::*;
-use chrono_tz::{UTC, Tz};
+use chrono_tz::{Tz, UTC};
use once_cell::sync::Lazy;
use regex::Regex;
use std::str::FromStr;
@@ -19,15 +19,24 @@ static RRULE_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)^(?:RRULE|EXRULE):"
static PARSE_LINE_RE_1: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)^\s+|\s+$").unwrap());
static PARSE_LINE_RE_2: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)^([A-Z]+?)[:;]").unwrap());
+static RDATE_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)RDATE(?:;TZID=([^:=]+))?").unwrap());
+static EXDATE_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?m)EXDATE(?:;TZID=([^:=]+))?").unwrap());
-fn parse_datestring_bit<T: FromStr>(bits: &regex::Captures, i: usize, dt: &str) -> Result<T, RRuleParseError> {
+static DATETIME_RE: Lazy<Regex> =
+ Lazy::new(|| Regex::new(r"(?m)(VALUE=DATE(-TIME)?)|(TZID=)").unwrap());
+
+fn parse_datestring_bit<T: FromStr>(
+ bits: &regex::Captures,
+ i: usize,
+ dt: &str,
+) -> Result<T, RRuleParseError> {
match bits.get(i) {
Some(bit) => match bit.as_str().parse::<T>() {
Err(_) => Err(RRuleParseError(format!("Invalid datetime: {}", dt))),
- Ok(val) => Ok(val)
- }
- _ => Err(RRuleParseError(format!("Invalid datetime: {}", dt)))
+ Ok(val) => Ok(val),
+ },
+ _ => Err(RRuleParseError(format!("Invalid datetime: {}", dt))),
}
}
@@ -56,7 +65,6 @@ fn datestring_to_date(dt: &str, tz: &Tz) -> Result<DTime, RRuleParseError> {
fn parse_dtstart(s: &str) -> Result<Options, RRuleParseError> {
let caps = DTSTART_RE.captures(s);
-
match caps {
Some(caps) => {
@@ -66,8 +74,13 @@ fn parse_dtstart(s: &str) -> Result<Options, RRuleParseError> {
UTC
};
+ let dtstart_str = match caps.get(2) {
+ Some(dt) => dt.as_str(),
+ None => return Err(RRuleParseError(format!("Invalid datetime: {}", s))),
+ };
+
let mut options = Options::new();
- options.dtstart = Some(datestring_to_date(caps.get(2).unwrap().as_str(), &tzid)?);
+ options.dtstart = Some(datestring_to_date(dtstart_str, &tzid)?);
options.tzid = Some(tzid);
Ok(options)
}
@@ -88,6 +101,14 @@ fn from_str_to_freq(s: &str) -> Option<Frequenzy> {
}
}
+fn stringval_to_int<T: FromStr>(val: &str, err_msg: String) -> Result<T, RRuleParseError> {
+ if let Ok(val) = val.parse() {
+ Ok(val)
+ } else {
+ return Err(RRuleParseError(err_msg));
+ }
+}
+
fn parse_rrule(line: &str) -> Result<Options, RRuleParseError> {
let stripped_line = if line.starts_with("RRULE:") {
&line[6..]
@@ -101,7 +122,6 @@ fn parse_rrule(line: &str) -> Result<Options, RRuleParseError> {
let attrs = attrs.split(";");
for attr in attrs {
-
let l: Vec<&str> = attr.split("=").collect();
let key = l[0];
@@ -109,54 +129,93 @@ fn parse_rrule(line: &str) -> Result<Options, RRuleParseError> {
if l.len() > 1 {
value = l[1];
}
-
+
match key.to_uppercase().as_str() {
- "FREQ" => {
- match from_str_to_freq(value) {
- Some(freq) => options.freq = Some(freq),
- None => return Err(RRuleParseError(format!("Invalid frequenzy: {}", value)))
- }
- }
+ "FREQ" => match from_str_to_freq(value) {
+ Some(freq) => options.freq = Some(freq),
+ None => return Err(RRuleParseError(format!("Invalid frequenzy: {}", value))),
+ },
"WKST" => {
- options.wkst = Some(value.parse::<usize>().unwrap());
+ let wkst = stringval_to_int(value, format!("Invalid weekstart value"))?;
+ if wkst > 6 {
+ return Err(RRuleParseError(format!(
+ "Invalid wkst value: {}. It must be between 0 and 6",
+ wkst
+ )));
+ };
+ options.wkst = Some(wkst);
}
"COUNT" => {
- options.count = Some(value.parse::<u32>().unwrap());
+ let count = stringval_to_int(value, format!("Invalid count"))?;
+ options.count = Some(count);
}
"INTERVAL" => {
- options.interval = Some(value.parse::<usize>().unwrap());
+ let interval = stringval_to_int(value, format!("Invalid interval"))?;
+ options.interval = Some(interval);
}
"BYSETPOS" => {
- let bysetpos = value.split(",").map(|d| d.parse::<isize>().unwrap()).collect();
- options.bysetpos = Some(bysetpos);
+ let mut bysetpos = vec![];
+ for val in value.split(",") {
+ let val = stringval_to_int(val, format!("Invalid bysetpos value"))?;
+ bysetpos.push(val);
+ }
+ options.bysetpos = Some(bysetpos);
}
"BYMONTH" => {
- let bymonth = value.split(",").map(|d| d.parse::<usize>().unwrap()).collect();
- options.bymonth = Some(bymonth);
+ let mut bymonth = vec![];
+ for val in value.split(",") {
+ let val = stringval_to_int(val, format!("Invalid bymonth value"))?;
+ bymonth.push(val);
+ }
+ options.bymonth = Some(bymonth);
}
- "BYMONTHDAY" => {
- let bymonthday = value.split(",").map(|d| d.parse::<isize>().unwrap()).collect();
- options.bymonthday = Some(bymonthday);
+ "BYMONTHDAY" => {
+ let mut bymonthday = vec![];
+ for val in value.split(",") {
+ let val = stringval_to_int(val, format!("Invalid bymonthday value"))?;
+ bymonthday.push(val);
+ }
+ options.bymonthday = Some(bymonthday);
}
"BYYEARDAY" => {
- let byyearday = value.split(",").map(|d| d.parse::<isize>().unwrap()).collect();
- options.byyearday = Some(byyearday);
+ let mut byyearday = vec![];
+ for val in value.split(",") {
+ let val = stringval_to_int(val, format!("Invalid byyearday value"))?;
+ byyearday.push(val);
+ }
+ options.byyearday = Some(byyearday);
}
"BYWEEKNO" => {
- let byweekno = value.split(",").map(|d| d.parse::<isize>().unwrap()).collect();
- options.byweekno = Some(byweekno);
+ let mut byweekno = vec![];
+ for val in value.split(",") {
+ let val = stringval_to_int(val, format!("Invalid byweekno value"))?;
+ byweekno.push(val);
+ }
+ options.byweekno = Some(byweekno);
}
- "BYHOUR" => {
- let byhour = value.split(",").map(|d| d.parse::<usize>().unwrap()).collect();
- options.byhour = Some(byhour);
+ "BYHOUR" => {
+ let mut byhour = vec![];
+ for val in value.split(",") {
+ let val = stringval_to_int(val, format!("Invalid byhour value"))?;
+ byhour.push(val);
+ }
+ options.byhour = Some(byhour);
}
- "BYMINUTE" => {
- let byminute = value.split(",").map(|d| d.parse::<usize>().unwrap()).collect();
- options.byminute = Some(byminute);
+ "BYMINUTE" => {
+ let mut byminute = vec![];
+ for val in value.split(",") {
+ let val = stringval_to_int(val, format!("Invalid byminute value"))?;
+ byminute.push(val);
+ }
+ options.byminute = Some(byminute);
}
"BYSECOND" => {
- let bysecond = value.split(",").map(|d| d.parse::<usize>().unwrap()).collect();
- options.bysecond = Some(bysecond);
+ let mut bysecond = vec![];
+ for val in value.split(",") {
+ let val = stringval_to_int(val, format!("Invalid bysecond value"))?;
+ bysecond.push(val);
+ }
+ options.bysecond = Some(bysecond);
}
"BYWEEKDAY" | "BYDAY" => {
options.byweekday = Some(parse_weekday(value)?);
@@ -172,7 +231,10 @@ fn parse_rrule(line: &str) -> Result<Options, RRuleParseError> {
options.until = Some(datestring_to_date(value, &UTC)?);
}
"BYEASTER" => {
- options.byeaster = Some(value.parse::<isize>().unwrap());
+ options.byeaster = Some(stringval_to_int(
+ value,
+ format!("Invalid byeaster val: {}", value),
+ )?);
}
_ => return Err(RRuleParseError(format!("Invalid property: {}", key))),
};
@@ -195,48 +257,52 @@ fn str_to_weekday(d: &str) -> Result<usize, RRuleParseError> {
}
fn parse_weekday(val: &str) -> Result<Vec<usize>, RRuleParseError> {
- val.split(",").map(|day| {
- if day.len() == 2 {
- // MO, TU, ...
- return str_to_weekday(day);
- }
+ val.split(",")
+ .map(|day| {
+ if day.len() == 2 {
+ // MO, TU, ...
+ return str_to_weekday(day);
+ }
+
+ // ! NOT SUPPORTED YET
+ // -1MO, +3FR, 1SO, 13TU ...
+ // let regex = Regex::new(r"(?m)^([+-]?\d{1,2})([A-Z]{2})$").unwrap();
+ // let parts = regex.captures(day).unwrap();
+ // let n = parts.get(1).unwrap();
+ // let wdaypart = parts.get(2).unwrap();
+ // let wday = str_to_weekday(d)
- // ! NOT SUPPORTED YET
- // -1MO, +3FR, 1SO, 13TU ...
- // let regex = Regex::new(r"(?m)^([+-]?\d{1,2})([A-Z]{2})$").unwrap();
- // let parts = regex.captures(day).unwrap();
- // let n = parts.get(1).unwrap();
- // let wdaypart = parts.get(2).unwrap();
- // let wday = str_to_weekday(d)
-
- return str_to_weekday(day);
- }).collect()
+ return str_to_weekday(day);
+ })
+ .collect()
}
fn parse_line(rfc_string: &str) -> Result<Option<Options>, RRuleParseError> {
- // let re = Regex::new(r"(?m)^\s+|\s+$").unwrap();
let rfc_string = PARSE_LINE_RE_1.replace(rfc_string, "");
if rfc_string.is_empty() {
return Ok(None);
}
- // let re = Regex::new(r"(?m)^([A-Z]+?)[:;]").unwrap();
let rfc_string_upper = rfc_string.to_uppercase();
let header = PARSE_LINE_RE_2.captures(&rfc_string_upper);
-
-
let rfc_string = rfc_string.to_string();
if header.is_none() {
return Ok(Some(parse_rrule(&rfc_string)?));
}
let header = header.unwrap();
- let key = header.get(1).unwrap().as_str();
+ let key = match header.get(1) {
+ Some(k) => k.as_str(),
+ None => return Err(RRuleParseError(format!("Invalid rfc_string: {}", rfc_string)))
+ };
match key {
"EXRULE" | "RRULE" => Ok(Some(parse_rrule(&rfc_string)?)),
"DTSTART" => Ok(Some(parse_dtstart(&rfc_string)?)),
- _ => Err(RRuleParseError(format!("Unsupported RFC prop {} in {}", key, &rfc_string)))
+ _ => Err(RRuleParseError(format!(
+ "Unsupported RFC prop {} in {}",
+ key, &rfc_string
+ ))),
}
}
@@ -244,7 +310,7 @@ fn parse_line(rfc_string: &str) -> Result<Option<Options>, RRuleParseError> {
struct ParsedLine {
name: String,
params: Vec<String>,
- value: String
+ value: String,
}
fn break_down_line(line: &str) -> ParsedLine {
@@ -254,20 +320,20 @@ fn break_down_line(line: &str) -> ParsedLine {
ParsedLine {
name: params[0].to_uppercase(),
params: params[1..].iter().map(|s| String::from(*s)).collect(),
- value: String::from(parsed_line_name.value)
+ value: String::from(parsed_line_name.value),
}
}
struct LineName {
name: String,
- value: String
+ value: String,
}
fn extract_name(line: String) -> LineName {
if !line.contains(":") {
return LineName {
name: String::from("RRULE"),
- value: line
+ value: line,
};
}
@@ -277,9 +343,9 @@ fn extract_name(line: String) -> LineName {
LineName {
name: String::from(name),
- value
+ value,
}
-}
+}
fn parse_string(rfc_string: &str) -> Result<Options, RRuleParseError> {
let mut options = vec![];
@@ -288,7 +354,6 @@ fn parse_string(rfc_string: &str) -> Result<Options, RRuleParseError> {
if let Some(parsed_line) = parsed_line {
options.push(parsed_line);
}
-
}
if options.len() == 1 {
@@ -298,6 +363,7 @@ fn parse_string(rfc_string: &str) -> Result<Options, RRuleParseError> {
Ok(Options::concat(&options[0], &options[1]))
}
+
#[derive(Debug)]
struct ParsedInput {
rrule_vals: Vec<Options>,
@@ -314,12 +380,7 @@ fn parse_input(s: &str) -> Result<ParsedInput, RRuleParseError> {
let mut exrule_vals = vec![];
let mut exdate_vals = vec![];
- let Options {
- dtstart,
- tzid,
- ..
- } = parse_dtstart(s)?;
-
+ let Options { dtstart, tzid, .. } = parse_dtstart(s)?;
let lines: Vec<&str> = s.split("\n").collect();
for line in &lines {
@@ -332,7 +393,7 @@ fn parse_input(s: &str) -> Result<ParsedInput, RRuleParseError> {
if parsed_line.value.is_empty() {
continue;
}
-
+
rrule_vals.push(parse_string(line)?);
}
"EXRULE" => {
@@ -342,58 +403,80 @@ fn parse_input(s: &str) -> Result<ParsedInput, RRuleParseError> {
if parsed_line.value.is_empty() {
continue;
}
- // TODO: why is it parsed_line.value here and line for RRULE ?? Do some testing
+ // TODO: why is it parsed_line.value here and line for RRULE ?? Do some testing
exrule_vals.push(parse_string(&parsed_line.value)?);
}
"RDATE" => {
- let re = Regex::new(r"(?m)RDATE(?:;TZID=([^:=]+))?").unwrap();
- let matches = re.captures(line).unwrap();
+ let matches = match RDATE_RE.captures(line) {
+ Some(m) => m,
+ None => return Err(RRuleParseError(format!("Invalid RDATE specified")))
+ };
let mut tz = UTC;
if let Some(tzid) = matches.get(1) {
tz = String::from(tzid.as_str()).parse().unwrap_or(UTC);
}
-
- rdate_vals.append(&mut parse_rdate(&parsed_line.value, parsed_line.params, &tz)?);
+
+ rdate_vals.append(&mut parse_rdate(
+ &parsed_line.value,
+ parsed_line.params,
+ &tz,
+ )?);
}
"EXDATE" => {
- let re = Regex::new(r"(?m)EXDATE(?:;TZID=([^:=]+))?").unwrap();
- let matches = re.captures(line).unwrap();
+ let matches = match EXDATE_RE.captures(line) {
+ Some(m) => m,
+ None => return Err(RRuleParseError(format!("Invalid EXDATE specified")))
+ };
let tz: Tz = if let Some(tzid) = matches.get(1) {
String::from(tzid.as_str()).parse().unwrap_or(UTC)
} else {
UTC
};
- exdate_vals.append(&mut parse_rdate(&parsed_line.value, parsed_line.params, &tz)?);
+ exdate_vals.append(&mut parse_rdate(
+ &parsed_line.value,
+ parsed_line.params,
+ &tz,
+ )?);
}
"DTSTART" => (),
- _ => return Err(RRuleParseError(format!("Unsupported property: {}", parsed_line.name)))
+ _ => {
+ return Err(RRuleParseError(format!(
+ "Unsupported property: {}",
+ parsed_line.name
+ )))
+ }
}
}
-
return Ok(ParsedInput {
dtstart,
tzid,
rrule_vals,
rdate_vals,
exrule_vals,
- exdate_vals
- })
+ exdate_vals,
+ });
}
-fn validate_date_param(params: Vec<&str>) -> Result<(), RRuleParseError>{
- let re = Regex::new(r"(?m)(VALUE=DATE(-TIME)?)|(TZID=)").unwrap();
-
+fn validate_date_param(params: Vec<&str>) -> Result<(), RRuleParseError> {
for param in &params {
- if re.captures(param).unwrap().len() == 0 {
- return Err(RRuleParseError(format!("Unsupported RDATE/EXDATE parm: {}", param)));
+ match DATETIME_RE.captures(param) {
+ Some(caps) if caps.len() > 0 => (),
+ _ => return Err(RRuleParseError(format!(
+ "Unsupported RDATE/EXDATE parm: {}",
+ param
+ )))
}
}
Ok(())
}
-// ! works needs to be done here
-fn parse_rdate(rdateval: &str, params: Vec<String>, tz: &Tz) -> Result<Vec<DTime>, RRuleParseError> {
+// ! work needs to be done here
+fn parse_rdate(
+ rdateval: &str,
+ params: Vec<String>,
+ tz: &Tz,
+) -> Result<Vec<DTime>, RRuleParseError> {
let params: Vec<&str> = params.iter().map(|p| p.as_str()).collect();
validate_date_param(params)?;
// let re_timezone = Regex::new(r"(?m)TZID=(.+):").unwrap();
@@ -407,7 +490,6 @@ fn parse_rdate(rdateval: &str, params: Vec<String>, tz: &Tz) -> Result<Vec<DTime
Ok(rdatevals)
}
-
pub fn build_rruleset(s: &str) -> Result<RRuleSet, RRuleParseError> {
let ParsedInput {
mut rrule_vals,
@@ -419,10 +501,9 @@ pub fn build_rruleset(s: &str) -> Result<RRuleSet, RRuleParseError> {
..
} = parse_input(s)?;
-
let mut rset = RRuleSet::new();
rset.dtstart = dtstart;
-
+
for rruleval in rrule_vals.iter_mut() {
rruleval.tzid = tzid.clone();
rruleval.dtstart = dtstart;
@@ -441,14 +522,13 @@ pub fn build_rruleset(s: &str) -> Result<RRuleSet, RRuleParseError> {
let parsed_opts = parse_options(&exrule)?;
let exrule = RRule::new(parsed_opts);
- rset.exrule(exrule);
+ rset.exrule(exrule);
}
for exdate in exdate_vals {
rset.exdate(exdate);
}
-
Ok(rset)
}
@@ -468,7 +548,6 @@ pub fn build_rrule(s: &str) -> Result<RRule, RRuleParseError> {
Ok(RRule::new(parsed_opts))
}
-
#[cfg(test)]
mod test {
use super::*;
@@ -495,6 +574,7 @@ mod test {
#[test]
fn it_works_4() {
let res = build_rruleset("DTSTART:20120201T120000Z\nRRULE:FREQ=DAILY;COUNT=5\nEXDATE;TZID=Europe/Berlin:20120202T130000Z,20120203T130000Z");
+ println!("{:?}", res);
assert!(res.is_ok());
}
@@ -511,7 +591,9 @@ mod test {
#[test]
fn exrule() {
- let res = build_rruleset("DTSTART:20120201T120000Z\nRRULE:FREQ=DAILY;COUNT=5\nEXRULE:FREQ=WEEKLY;INTERVAL=2");
+ let res = build_rruleset(
+ "DTSTART:20120201T120000Z\nRRULE:FREQ=DAILY;COUNT=5\nEXRULE:FREQ=WEEKLY;INTERVAL=2",
+ );
assert!(res.is_ok());
let res = res.unwrap();
assert_eq!(res.exrule.len(), 1);
@@ -519,24 +601,18 @@ mod test {
assert_eq!(res.exrule[0].options.freq, Frequenzy::Weekly);
}
-
////////////////////////////////////////////////////
// Invalid stuff
////////////////////////////////////////////////////
#[test]
fn garbage_strings() {
- let test_cases = vec![
- "helloworld",
- "foo bar",
- "hello\nworld",
- "RRUle:test",
- ];
+ let test_cases = vec!["helloworld", "foo bar", "hello\nworld", "RRUle:test"];
for test_case in &test_cases {
let res = build_rruleset(test_case);
assert!(res.is_err());
}
}
-
+
#[test]
fn invalid_dtstart() {
let res = build_rruleset("DTSTART:20120201120000Z\nRRULE:FREQ=DAILY;COUNT=5");
@@ -557,7 +633,7 @@ mod test {
let now = std::time::SystemTime::now();
for _ in 0..10000 {
let mut res = build_rruleset("RRULE:UNTIL=19990404T110000Z;DTSTART;TZID=America/New_York:19990104T110000Z;FREQ=WEEKLY;BYDAY=TU,WE").unwrap();
-
+
// println!("Parsing took: {:?}", now.elapsed().unwrap().as_millis());
let tmp_now = std::time::SystemTime::now();
@@ -565,7 +641,5 @@ mod test {
println!("All took: {:?}", tmp_now.elapsed().unwrap().as_nanos());
}
println!("Time took: {:?}", now.elapsed().unwrap().as_millis());
-
}
}
-
diff --git a/tests/rrule.rs b/tests/rrule.rs
index 38a76e2..439f29b 100644
--- a/tests/rrule.rs
+++ b/tests/rrule.rs
@@ -3,8 +3,8 @@ extern crate chrono_tz;
extern crate rrule;
use chrono::prelude::*;
-use chrono_tz::{UTC, Tz};
-use rrule::{RRule, ParsedOptions, Frequenzy};
+use chrono_tz::{Tz, UTC};
+use rrule::{Frequenzy, ParsedOptions, RRule};
#[cfg(test)]
mod test {