diff options
author | Fredrik Meringdal <fmeringdal@hotmail.com> | 2020-12-04 16:13:58 +0100 |
---|---|---|
committer | Fredrik Meringdal <fmeringdal@hotmail.com> | 2020-12-04 16:13:58 +0100 |
commit | 3931287312234b6a5f5da225c2a5b30f8d29e3c6 (patch) | |
tree | 7cd4549e9a23a468e5e4db2d013528445cfa2e07 /src/rrulestr.rs | |
parent | c437b568f9dad67b212a9b17d2ac32dd40263571 (diff) | |
download | rust_rrule-3931287312234b6a5f5da225c2a5b30f8d29e3c6.zip |
support for nweekday
Diffstat (limited to 'src/rrulestr.rs')
-rw-r--r-- | src/rrulestr.rs | 92 |
1 files changed, 61 insertions, 31 deletions
diff --git a/src/rrulestr.rs b/src/rrulestr.rs index 1d263ed..756a5ae 100644 --- a/src/rrulestr.rs +++ b/src/rrulestr.rs @@ -1,9 +1,9 @@ +use crate::datetime::get_weekday_val; use crate::datetime::DTime; use crate::options::*; use crate::parse_options::parse_options; use crate::rrule::RRule; use crate::rruleset::RRuleSet; -use crate::datetime::get_weekday_val; use chrono::prelude::*; use chrono_tz::{Tz, UTC}; use regex::Regex; @@ -21,6 +21,7 @@ lazy_static! { static ref RDATE_RE: Regex = Regex::new(r"(?m)RDATE(?:;TZID=([^:=]+))?").unwrap(); static ref EXDATE_RE: Regex = Regex::new(r"(?m)EXDATE(?:;TZID=([^:=]+))?").unwrap(); static ref DATETIME_RE: Regex = Regex::new(r"(?m)(VALUE=DATE(-TIME)?)|(TZID=)").unwrap(); + static ref NWEEKDAY_REGEX: Regex = Regex::new(r"(?m)^([+-]?\d{1,2})([A-Z]{2})$").unwrap(); } fn parse_datestring_bit<T: FromStr>( @@ -38,7 +39,6 @@ fn parse_datestring_bit<T: FromStr>( } fn datestring_to_date(dt: &str, tz: &Tz) -> Result<DTime, RRuleParseError> { - let bits = DATESTR_RE.captures(dt); if bits.is_none() { return Err(RRuleParseError(format!("Invalid datetime: {}", dt))); @@ -71,7 +71,7 @@ fn parse_dtstart(s: &str) -> Result<Options, RRuleParseError> { } else { UTC }; - + let dtstart_str = match caps.get(2) { Some(dt) => dt.as_str(), None => return Err(RRuleParseError(format!("Invalid datetime: {}", s))), @@ -154,16 +154,14 @@ fn parse_rrule(line: &str) -> Result<Options, RRuleParseError> { Some(freq) => options.freq = Some(freq), None => return Err(RRuleParseError(format!("Invalid frequenzy: {}", value))), }, - "WKST" => { - match weekday_from_str(value) { - Ok(weekday) => { - options.wkst = Some(get_weekday_val(&weekday)); - } - Err(e) => { - return Err(RRuleParseError(e)); - } + "WKST" => match weekday_from_str(value) { + Ok(weekday) => { + options.wkst = Some(get_weekday_val(&weekday)); } - } + Err(e) => { + return Err(RRuleParseError(e)); + } + }, "COUNT" => { let count = stringval_to_int(value, format!("Invalid count"))?; options.count = Some(count); @@ -266,25 +264,23 @@ 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); - } +fn parse_weekday(val: &str) -> Result<Vec<NWeekday>, RRuleParseError> { + let mut wdays = vec![]; + for day in val.split(",") { + if day.len() == 2 { + // MO, TU, ... + let wday = str_to_weekday(day)?; + wdays.push(NWeekday::new(wday, 1)); + continue; + } - // ! 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() + let parts = NWEEKDAY_REGEX.captures(day).unwrap(); + let n = parts.get(1).unwrap().as_str().parse().unwrap(); + let wdaypart = parts.get(2).unwrap(); + let wday = str_to_weekday(wdaypart.as_str())?; + wdays.push(NWeekday::new(wday, n)); + } + Ok(wdays) } fn parse_line(rfc_string: &str) -> Result<Option<Options>, RRuleParseError> { @@ -504,7 +500,8 @@ fn parse_rdate( } fn preprocess_rrule_string(s: &str) -> String { - s.replace("DTSTART;VALUE=DATETIME", "DTSTART").replace("DTSTART;VALUE=DATE", "DTSTART") + s.replace("DTSTART;VALUE=DATETIME", "DTSTART") + .replace("DTSTART;VALUE=DATE", "DTSTART") } pub fn build_rruleset(s: &str) -> Result<RRuleSet, RRuleParseError> { @@ -677,6 +674,39 @@ mod test { } #[test] + fn parses_byday_with_n() { + let cases = vec![ + "DTSTART:20200901T174500\nRRULE:FREQ=MONTHLY;UNTIL=20210504T154500Z;INTERVAL=1;BYDAY=1TU", + "DTSTART;VALUE=DATE:20200902\nRRULE:FREQ=MONTHLY;UNTIL=20210504T220000Z;INTERVAL=1;BYDAY=1WE", + "DTSTART:20200902T100000\nRRULE:FREQ=MONTHLY;UNTIL=20210505T080000Z;INTERVAL=1;BYDAY=1WE", + "DTSTART;VALUE=DATE:20200812\nRRULE:FREQ=MONTHLY;UNTIL=20210524T090000Z;INTERVAL=1;BYDAY=4MO" + ]; + for case in &cases { + let res = build_rruleset(case); + assert!(res.is_ok()); + } + let cases = vec![ + "RRULE:FREQ=MONTHLY;UNTIL=20210504T154500Z;INTERVAL=1;BYDAY=1TU", + "RRULE:FREQ=MONTHLY;UNTIL=20210504T220000Z;INTERVAL=1;BYDAY=1WE", + "RRULE:FREQ=MONTHLY;UNTIL=20210505T080000Z;INTERVAL=1;BYDAY=-1WE", + "RRULE:FREQ=MONTHLY;UNTIL=20210505T080000Z;INTERVAL=1;BYDAY=12SU", + "RRULE:FREQ=MONTHLY;UNTIL=20210524T090000Z;INTERVAL=1;BYDAY=4MO", + ]; + let opts = vec![ + vec![NWeekday::new(1,1)], + vec![NWeekday::new(2,1)], + vec![NWeekday::new(2,-1)], + vec![NWeekday::new(6, 12)], + vec![NWeekday::new(0,4)] + ]; + for i in 0..cases.len() { + let opts_or_err = parse_string(cases[i]); + assert!(opts_or_err.is_ok()); + assert_eq!(opts_or_err.unwrap().byweekday.unwrap(), opts[i]); + } + } + + #[test] #[ignore = "Only for benching"] fn bench() { let now = std::time::SystemTime::now(); |