From 15acffa779ff21c53752149bd9d248f769a0b7a3 Mon Sep 17 00:00:00 2001 From: Fredrik Meringdal Date: Wed, 14 Oct 2020 15:12:28 +0200 Subject: iterinfo --- src/iterinfo.rs | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 + src/masks.rs | 36 ++++++++--------- src/monthinfo.rs | 106 +++++++++++++++++++++++++++++++++++++++++++++++++ src/yearinfo.rs | 84 +++++++++++++++++++-------------------- 5 files changed, 286 insertions(+), 60 deletions(-) create mode 100644 src/iterinfo.rs create mode 100644 src/monthinfo.rs (limited to 'src') diff --git a/src/iterinfo.rs b/src/iterinfo.rs new file mode 100644 index 0000000..bf7f1d7 --- /dev/null +++ b/src/iterinfo.rs @@ -0,0 +1,118 @@ +use crate::monthinfo::*; +use crate::yearinfo::*; + +struct IterInfo<'a> { + pub yearinfo: Option, + pub monthinfo: Option, + options: &'a ParsedOptions, +} + +impl<'a> IterInfo<'a> { + pub fn new(options: &'a ParsedOptions) -> Self { + Self { + options, + yearinfo: None, + monthinfo: None, + } + } + + pub fn rebuild(&mut self, year: isize, month: usize) { + if self.monthinfo.is_none() || year != self.monthinfo.as_ref().unwrap().lastyear { + self.yearinfo = Some(rebuild_year(year as i32, self.options)); + } + + if !self.options.bynweekday.is_empty() + && ((self.monthinfo.is_none() || month != self.monthinfo.as_ref().unwrap().lastmonth) + || (self.monthinfo.is_none() || year != self.monthinfo.as_ref().unwrap().lastyear)) + { + if let Some(yearinfo) = &self.yearinfo { + self.monthinfo = Some(rebuild_month( + year, + month, + yearinfo.yearlen, + &yearinfo.mrange, + &yearinfo.wdaymask, + self.options, + )); + } + } + } + + pub fn lastyear(&self) -> Option { + match &self.monthinfo { + Some(info) => Some(info.lastyear), + None => None, + } + } + pub fn lastmonth(&self) -> Option { + match &self.monthinfo { + Some(info) => Some(info.lastmonth), + None => None, + } + } + pub fn yearlen(&self) -> Option { + match &self.yearinfo { + Some(info) => Some(info.yearlen), + None => None, + } + } + pub fn yearordinal(&self) -> Option { + match &self.yearinfo { + Some(info) => Some(info.yearordinal), + None => None, + } + } + pub fn mrange(&self) -> Option<&Vec> { + match &self.yearinfo { + Some(info) => Some(&info.mrange), + None => None, + } + } + pub fn wdaymask(&self) -> Option<&Vec> { + match &self.yearinfo { + Some(info) => Some(&info.wdaymask), + None => None, + } + } + + pub fn mmask(&self) -> Option<&Vec> { + match &self.yearinfo { + Some(info) => Some(&info.mmask), + None => None, + } + } + + pub fn wnomask(&self) -> Option<&Vec> { + match &self.yearinfo { + Some(info) => match &info.wnomask { + Some(mask) => Some(mask), + None => None, + }, + None => None, + } + } + pub fn nwdaymask(&self) -> Option<&Vec> { + match &self.monthinfo { + Some(info) => Some(&info.nwdaymask), + None => None, + } + } + pub fn nextyearlen(&self) -> Option { + match &self.yearinfo { + Some(info) => Some(info.nextyearlen), + None => None, + } + } + pub fn mdaymask(&self) -> Option<&Vec> { + match &self.yearinfo { + Some(info) => Some(&info.mdaymask), + None => None, + } + } + pub fn nmdaymask(&self) -> Option<&Vec> { + match &self.yearinfo { + Some(info) => Some(&info.nmdaymask), + None => None, + } + } +} diff --git a/src/lib.rs b/src/lib.rs index ede868c..5c7c03b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,6 @@ extern crate chrono; +mod iterinfo; mod masks; +mod monthinfo; mod yearinfo; diff --git a/src/masks.rs b/src/masks.rs index e2ca977..75730d8 100644 --- a/src/masks.rs +++ b/src/masks.rs @@ -5,31 +5,31 @@ // Every mask is 7 days longer to handle cross-year weekly periods. pub struct Masks { - pub WDAY: Vec, - pub M365: Vec, - pub M365RANGE: Vec, - pub M366: Vec, - pub M366RANGE: Vec, - pub MDAY365: Vec, - pub MDAY366: Vec, - pub NMDAY365: Vec, - pub NMDAY366: Vec, + pub WDAY: Vec, + pub M365: Vec, + pub M365RANGE: Vec, + pub M366: Vec, + pub M366RANGE: Vec, + pub MDAY365: Vec, + pub MDAY366: Vec, + pub NMDAY365: Vec, + pub NMDAY366: Vec, } impl Masks { pub fn new() -> Self { - let M28: Vec = (1..29).collect(); - let M29: Vec = (1..30).collect(); - let M30: Vec = (1..31).collect(); - let M31: Vec = (1..32).collect(); + let M28: Vec = (1..29).collect(); + let M29: Vec = (1..30).collect(); + let M30: Vec = (1..31).collect(); + let M31: Vec = (1..32).collect(); - let NM28: Vec = (-28..0).collect(); - let NM29: Vec = (-29..0).collect(); - let NM30: Vec = (-30..0).collect(); - let NM31: Vec = (-31..0).collect(); + let NM28: Vec = (-28..0).collect(); + let NM29: Vec = (-29..0).collect(); + let NM30: Vec = (-30..0).collect(); + let NM31: Vec = (-31..0).collect(); Self { - WDAY: vec![(0..7).collect::>(); 55] + WDAY: vec![(0..7).collect::>(); 55] .into_iter() .flatten() .collect(), diff --git a/src/monthinfo.rs b/src/monthinfo.rs new file mode 100644 index 0000000..3b82ee5 --- /dev/null +++ b/src/monthinfo.rs @@ -0,0 +1,106 @@ +use crate::masks::Masks; +use crate::yearinfo::*; +use chrono::prelude::*; +use chrono::{DateTime, Duration}; + +struct RRule {} + +#[derive(Debug)] +pub struct MonthInfo { + pub lastyear: isize, + pub lastmonth: usize, + pub nwdaymask: Vec, +} + +pub fn rebuild_month( + year: isize, + month: usize, + yearlen: usize, + mrange: &Vec, + wdaymask: &Vec, + options: &ParsedOptions, +) -> MonthInfo { + let mut result = MonthInfo { + lastyear: year, + lastmonth: month, + nwdaymask: vec![], + }; + + let mut ranges: Vec<(isize, isize)> = vec![]; + if options.freq == Frequenzy::YEARLY { + if options.bymonth.is_empty() { + ranges = vec![(0, year as isize)]; + } else { + for j in 0..options.bymonth.len() { + let m = options.bymonth[j]; + ranges.push((mrange[m - 1] as isize, mrange[m] as isize)) + } + } + } else if options.freq == Frequenzy::MONTHLY { + ranges.push((mrange[month - 1] as isize, mrange[month] as isize)); + } + + if ranges.is_empty() { + return result; + } + + // Weekly frequency won't get here, so we may not + // care about cross-year weekly periods. + result.nwdaymask = vec![0; yearlen]; + + for j in 0..ranges.len() { + let rang = ranges[j]; + let first = rang.0; + let last = rang.1 - 1; + + for k in 0..options.bynweekday.len() { + let mut i: isize; + let wday = options.bynweekday[k][0]; + let n = options.bynweekday[k][1]; + if n < 0 { + i = last + (n + 1) * 7; + i -= pymod(wdaymask[i as usize] as isize - wday, 7); + } else { + i = first + (n - 1) * 7; + i += pymod(7 - wdaymask[i as usize] as isize + wday, 7); + } + if first <= i && i <= last { + result.nwdaymask[i as usize] = 1; + } + } + } + + result +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let options = ParsedOptions { + freq: Frequenzy::YEARLY, + interval: 1, + count: Some(10), + until: None, + tzid: None, + dtstart: Utc.ymd(2020, 1, 1).and_hms(0, 0, 0), + wkst: 0, + bysetpos: vec![], + bymonth: vec![], + bymonthday: vec![], + bynmonthday: vec![], + byyearday: vec![], + byweekno: vec![1], + byweekday: vec![], + byhour: vec![], + byminute: vec![], + bysecond: vec![], + bynweekday: vec![], + }; + let res = rebuild_month(2020, 1, 366, &vec![], &vec![], &options); + println!("Res: {:?}", res); + assert_eq!(2 + 2, 4); + } +} diff --git a/src/yearinfo.rs b/src/yearinfo.rs index ee398e7..37767c1 100644 --- a/src/yearinfo.rs +++ b/src/yearinfo.rs @@ -2,24 +2,22 @@ use crate::masks::Masks; use chrono::prelude::*; use chrono::{DateTime, Duration}; -struct RRule {} - #[derive(Debug)] -struct YearInfo { - yearlen: usize, - nextyearlen: usize, - yearordinal: i64, - yearweekday: usize, - mmask: Vec, - mrange: Vec, - mdaymask: Vec, - nmdaymask: Vec, - wdaymask: Vec, - wnomask: Option>, +pub struct YearInfo { + pub yearlen: usize, + pub nextyearlen: usize, + pub yearordinal: isize, + pub yearweekday: usize, + pub mmask: Vec, + pub mrange: Vec, + pub mdaymask: Vec, + pub nmdaymask: Vec, + pub wdaymask: Vec, + pub wnomask: Option>, } -#[derive(Debug)] -enum Frequenzy { +#[derive(Debug, PartialEq)] +pub enum Frequenzy { YEARLY, MONTHLY, WEEKLY, @@ -30,24 +28,25 @@ enum Frequenzy { } #[derive(Debug)] -struct ParsedOptions { - freq: Frequenzy, - interval: usize, - count: Option, - until: Option>, - tzid: Option, - dtstart: DateTime, - wkst: usize, - bysetpos: Vec, - bymonth: Vec, - bymonthday: Vec, - bynmonthday: Vec, - byyearday: Vec, - byweekno: Vec, - byweekday: Vec, - byhour: Vec, - byminute: Vec, - bysecond: Vec, +pub struct ParsedOptions { + pub freq: Frequenzy, + pub interval: usize, + pub count: Option, + pub until: Option>, + pub tzid: Option, + pub dtstart: DateTime, + pub wkst: usize, + pub bysetpos: Vec, + pub bymonth: Vec, + pub bymonthday: Vec, + pub bynmonthday: Vec, + pub byyearday: Vec, + pub byweekno: Vec, + pub byweekday: Vec, + pub byhour: Vec, + pub byminute: Vec, + pub bysecond: Vec, + pub bynweekday: Vec>, } fn is_leap_year(year: i32) -> bool { @@ -61,8 +60,8 @@ fn get_year_len(year: i32) -> usize { 365 } -fn to_ordinal(date: &DateTime) -> i64 { - date.timestamp() / 60 / 60 / 24 +fn to_ordinal(date: &DateTime) -> isize { + (date.timestamp() / 60 / 60 / 24) as isize } fn get_weekday_val(wk: &Weekday) -> usize { @@ -77,12 +76,12 @@ fn get_weekday_val(wk: &Weekday) -> usize { } } -struct BaseMasks { - mmask: Vec, - mdaymask: Vec, - nmdaymask: Vec, - wdaymask: Vec, - mrange: Vec, +pub struct BaseMasks { + mmask: Vec, + mdaymask: Vec, + nmdaymask: Vec, + wdaymask: Vec, + mrange: Vec, } fn base_year_masks(year: i32) -> BaseMasks { @@ -119,7 +118,7 @@ pub fn pymod(a: isize, b: isize) -> isize { r } -fn rebuild_year(year: i32, options: &ParsedOptions) -> YearInfo { +pub fn rebuild_year(year: i32, options: &ParsedOptions) -> YearInfo { let firstyday = Utc.ymd(year, 1, 1).and_hms_milli(0, 0, 0, 0); let yearlen = get_year_len(year); @@ -276,6 +275,7 @@ mod tests { byhour: vec![], byminute: vec![], bysecond: vec![], + bynweekday: vec![], }; let res = rebuild_year(2020, &options); println!("Res: {:?}", res); -- cgit v1.2.3