diff options
-rw-r--r-- | src/iterinfo.rs | 118 | ||||
-rw-r--r-- | src/lib.rs | 2 | ||||
-rw-r--r-- | src/masks.rs | 36 | ||||
-rw-r--r-- | src/monthinfo.rs | 106 | ||||
-rw-r--r-- | src/yearinfo.rs | 84 |
5 files changed, 286 insertions, 60 deletions
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<YearInfo>, + pub monthinfo: Option<MonthInfo>, + 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<isize> { + match &self.monthinfo { + Some(info) => Some(info.lastyear), + None => None, + } + } + pub fn lastmonth(&self) -> Option<usize> { + match &self.monthinfo { + Some(info) => Some(info.lastmonth), + None => None, + } + } + pub fn yearlen(&self) -> Option<usize> { + match &self.yearinfo { + Some(info) => Some(info.yearlen), + None => None, + } + } + pub fn yearordinal(&self) -> Option<isize> { + match &self.yearinfo { + Some(info) => Some(info.yearordinal), + None => None, + } + } + pub fn mrange(&self) -> Option<&Vec<usize>> { + match &self.yearinfo { + Some(info) => Some(&info.mrange), + None => None, + } + } + pub fn wdaymask(&self) -> Option<&Vec<usize>> { + match &self.yearinfo { + Some(info) => Some(&info.wdaymask), + None => None, + } + } + + pub fn mmask(&self) -> Option<&Vec<usize>> { + match &self.yearinfo { + Some(info) => Some(&info.mmask), + None => None, + } + } + + pub fn wnomask(&self) -> Option<&Vec<usize>> { + match &self.yearinfo { + Some(info) => match &info.wnomask { + Some(mask) => Some(mask), + None => None, + }, + None => None, + } + } + pub fn nwdaymask(&self) -> Option<&Vec<isize>> { + match &self.monthinfo { + Some(info) => Some(&info.nwdaymask), + None => None, + } + } + pub fn nextyearlen(&self) -> Option<usize> { + match &self.yearinfo { + Some(info) => Some(info.nextyearlen), + None => None, + } + } + pub fn mdaymask(&self) -> Option<&Vec<usize>> { + match &self.yearinfo { + Some(info) => Some(&info.mdaymask), + None => None, + } + } + pub fn nmdaymask(&self) -> Option<&Vec<isize>> { + match &self.yearinfo { + Some(info) => Some(&info.nmdaymask), + None => None, + } + } +} @@ -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<u32>, - pub M365: Vec<u32>, - pub M365RANGE: Vec<u32>, - pub M366: Vec<u32>, - pub M366RANGE: Vec<u32>, - pub MDAY365: Vec<u32>, - pub MDAY366: Vec<u32>, - pub NMDAY365: Vec<i32>, - pub NMDAY366: Vec<i32>, + pub WDAY: Vec<usize>, + pub M365: Vec<usize>, + pub M365RANGE: Vec<usize>, + pub M366: Vec<usize>, + pub M366RANGE: Vec<usize>, + pub MDAY365: Vec<usize>, + pub MDAY366: Vec<usize>, + pub NMDAY365: Vec<isize>, + pub NMDAY366: Vec<isize>, } impl Masks { pub fn new() -> Self { - let M28: Vec<u32> = (1..29).collect(); - let M29: Vec<u32> = (1..30).collect(); - let M30: Vec<u32> = (1..31).collect(); - let M31: Vec<u32> = (1..32).collect(); + let M28: Vec<usize> = (1..29).collect(); + let M29: Vec<usize> = (1..30).collect(); + let M30: Vec<usize> = (1..31).collect(); + let M31: Vec<usize> = (1..32).collect(); - let NM28: Vec<i32> = (-28..0).collect(); - let NM29: Vec<i32> = (-29..0).collect(); - let NM30: Vec<i32> = (-30..0).collect(); - let NM31: Vec<i32> = (-31..0).collect(); + let NM28: Vec<isize> = (-28..0).collect(); + let NM29: Vec<isize> = (-29..0).collect(); + let NM30: Vec<isize> = (-30..0).collect(); + let NM31: Vec<isize> = (-31..0).collect(); Self { - WDAY: vec![(0..7).collect::<Vec<u32>>(); 55] + WDAY: vec![(0..7).collect::<Vec<usize>>(); 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<isize>, +} + +pub fn rebuild_month( + year: isize, + month: usize, + yearlen: usize, + mrange: &Vec<usize>, + wdaymask: &Vec<usize>, + 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<u32>, - mrange: Vec<u32>, - mdaymask: Vec<u32>, - nmdaymask: Vec<i32>, - wdaymask: Vec<u32>, - wnomask: Option<Vec<u32>>, +pub struct YearInfo { + pub yearlen: usize, + pub nextyearlen: usize, + pub yearordinal: isize, + pub yearweekday: usize, + pub mmask: Vec<usize>, + pub mrange: Vec<usize>, + pub mdaymask: Vec<usize>, + pub nmdaymask: Vec<isize>, + pub wdaymask: Vec<usize>, + pub wnomask: Option<Vec<usize>>, } -#[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<u32>, - until: Option<DateTime<Utc>>, - tzid: Option<String>, - dtstart: DateTime<Utc>, - wkst: usize, - bysetpos: Vec<u32>, - bymonth: Vec<u32>, - bymonthday: Vec<u32>, - bynmonthday: Vec<u32>, - byyearday: Vec<u32>, - byweekno: Vec<isize>, - byweekday: Vec<u32>, - byhour: Vec<u32>, - byminute: Vec<u32>, - bysecond: Vec<u32>, +pub struct ParsedOptions { + pub freq: Frequenzy, + pub interval: usize, + pub count: Option<u32>, + pub until: Option<DateTime<Utc>>, + pub tzid: Option<String>, + pub dtstart: DateTime<Utc>, + pub wkst: usize, + pub bysetpos: Vec<usize>, + pub bymonth: Vec<usize>, + pub bymonthday: Vec<usize>, + pub bynmonthday: Vec<isize>, + pub byyearday: Vec<usize>, + pub byweekno: Vec<isize>, + pub byweekday: Vec<usize>, + pub byhour: Vec<usize>, + pub byminute: Vec<usize>, + pub bysecond: Vec<usize>, + pub bynweekday: Vec<Vec<isize>>, } fn is_leap_year(year: i32) -> bool { @@ -61,8 +60,8 @@ fn get_year_len(year: i32) -> usize { 365 } -fn to_ordinal(date: &DateTime<Utc>) -> i64 { - date.timestamp() / 60 / 60 / 24 +fn to_ordinal(date: &DateTime<Utc>) -> 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<u32>, - mdaymask: Vec<u32>, - nmdaymask: Vec<i32>, - wdaymask: Vec<u32>, - mrange: Vec<u32>, +pub struct BaseMasks { + mmask: Vec<usize>, + mdaymask: Vec<usize>, + nmdaymask: Vec<isize>, + wdaymask: Vec<usize>, + mrange: Vec<usize>, } 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); |