diff --git a/ifc/Cargo.lock b/ifc/Cargo.lock index 2d31440..5e8465a 100644 --- a/ifc/Cargo.lock +++ b/ifc/Cargo.lock @@ -171,6 +171,7 @@ dependencies = [ "params", "router", "serde", + "thiserror", ] [[package]] @@ -816,6 +817,26 @@ dependencies = [ "remove_dir_all", ] +[[package]] +name = "thiserror" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "time" version = "0.1.44" diff --git a/ifc/Cargo.toml b/ifc/Cargo.toml index 9c3d902..a75b2ee 100644 --- a/ifc/Cargo.toml +++ b/ifc/Cargo.toml @@ -10,18 +10,19 @@ license = "GPL-3.0-only" license-file = "../COPYING" [dependencies] -chrono = "0.4" -chrono-tz = "0.6" -iron = "0.6.1" -mustache = "0.9.0" -params = "*" -router = "*" -serde = { version = "1.0", features = ["derive"] } +chrono = { version = "0.4" } +chrono-tz = { version = "0.6" } +iron = { version = "0.6.1" } +mustache = { version = "0.9.0" } +params = { version = "*" } +router = { version = "*" } +serde = { version = "1.0", features = ["derive"] } +thiserror = { version = "1" } -[[bin]] -name = "ifc-today" -path = "src/today.rs" +# [[bin]] +# name = "ifc-today" +# path = "src/today.rs" -[[bin]] -name = "ifc-web" -path = "src/web.rs" +# [[bin]] +# name = "ifc-web" +# path = "src/web.rs" diff --git a/ifc/src/lib.rs b/ifc/src/lib.rs index d807833..30db983 100644 --- a/ifc/src/lib.rs +++ b/ifc/src/lib.rs @@ -10,10 +10,15 @@ Luminescent Dreams Tools is distributed in the hope that it will be useful, but You should have received a copy of the GNU General Public License along with Lumeto. If not, see . */ -extern crate chrono; -extern crate chrono_tz; - use chrono::{Datelike, NaiveDate}; +use serde::{Deserialize, Serialize}; +use thiserror::Error; + +#[derive(Clone, Debug, Error, PartialEq)] +pub enum Error { + #[error("invalid date")] + InvalidDate, +} #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum DayOfWeek { @@ -104,6 +109,110 @@ impl From for String { } } +fn is_leap_year(year: i32) -> bool { + NaiveDate::from_ymd(year, 12, 31).ordinal() == 366 +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub enum IFC { + LeapDay(i32), + YearDay(i32), + Day(Day), +} + +impl IFC { + fn leap_day(year: i32) -> Result { + if is_leap_year(year) { + Ok(Self::LeapDay(year)) + } else { + Err(Error::InvalidDate) + } + } + + fn year_day(year: i32) -> Self { + Self::YearDay(year) + } + + fn ymd(year: i32, month: u8, day: u8) -> Result { + if month < 1 || month > 13 { + Err(Error::InvalidDate) + } else if day < 1 || day > 28 { + Err(Error::InvalidDate) + } else { + Ok(Self::Day(Day { year, month, day })) + } + } + + fn day_of_week(&self) -> DayOfWeek { + match *self { + IFC::LeapDay(_) => DayOfWeek::LeapDay, + IFC::YearDay(_) => DayOfWeek::YearDay, + IFC::Day(Day { day, .. }) => match (day - 1) % 7 { + 0 => DayOfWeek::Sunday, + 1 => DayOfWeek::Monday, + 2 => DayOfWeek::Tuesday, + 3 => DayOfWeek::Wednesday, + 4 => DayOfWeek::Thursday, + 5 => DayOfWeek::Friday, + 6 => DayOfWeek::Saturday, + _ => panic!("impossible calculation"), + }, + } + } + + fn day_ordinal(&self) -> u32 { + self.day_ordinal0() + 1 + } + + fn day_ordinal0(&self) -> u32 { + match *self { + IFC::LeapDay(_) => 168, + IFC::YearDay(year) => { + if is_leap_year(year) { + 365 + } else { + 364 + } + } + IFC::Day(Day { year, month, day }) => { + u32::from(month - 1) * 28 + + u32::from(day - 1) + + if is_leap_year(year) && month > 6 { + 1 + } else { + 0 + } + } + } + } + + fn week_ordinal(&self) -> u32 { + self.week_ordinal0() + 1 + } + + fn week_ordinal0(&self) -> u32 { + match *self { + IFC::LeapDay(_) => 0, + IFC::YearDay(_) => 0, + IFC::Day(Day { month, day, .. }) => u32::from(month - 1) * 4 + (u32::from(day - 1) / 7), + } + } +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Day { + year: i32, + month: u8, + day: u8, +} + +impl From for Day { + fn from(date: chrono::NaiveDate) -> Self { + unimplemented!(); + } +} + +/* #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct IFC { year: u32, @@ -111,12 +220,9 @@ pub struct IFC { leap_year: bool, } -fn is_leap_year(year: i32) -> bool { - NaiveDate::from_ymd(year, 12, 31).ordinal() == 366 -} impl IFC { - pub fn ymd(year: u32, month: u8, day: u8) -> IFC { + pub fn ymd(year: u32, month: u8, day: u8) -> Self { let leap_year = is_leap_year(year as i32 - 10000); let ordinal = if is_leap_year(year as i32 - 10000) { @@ -138,7 +244,18 @@ impl IFC { } } - pub fn weekday_ifc(&self) -> DayOfWeek { + pub fn year_day(year: u32) -> Self { + Self { + year, + ordinal: if is_leap_year(year) { 366 } else { 365 } + } + } + + pub fn leap_day(year: u32) -> Result { + } + + pub fn weekday(&self) -> DayOfWeek { + // if self.day.ordinal == match self.day() % 7 { 0 => DayOfWeek::Saturday, 1 => DayOfWeek::Sunday, @@ -155,7 +272,9 @@ impl IFC { Month::from(self.month()) } } +*/ +/* impl From> for IFC { fn from(d: chrono::Date) -> IFC { IFC::from(d.naive_utc()) @@ -178,7 +297,9 @@ impl From for IFC { } } } +*/ +/* impl Datelike for IFC { fn year(&self) -> i32 { self.year as i32 @@ -276,6 +397,7 @@ impl Datelike for IFC { }) } } +*/ #[cfg(test)] mod tests { @@ -283,6 +405,188 @@ mod tests { use chrono::NaiveDate; + #[test] + fn creates_a_day() { + assert_eq!(IFC::leap_day(12020), Ok(IFC::LeapDay(12020))); + assert_eq!(IFC::year_day(12020), IFC::YearDay(12020)); + assert_eq!( + IFC::ymd(12022, 13, 28), + Ok(IFC::Day(Day { + year: 12022, + month: 13, + day: 28 + })) + ); + } + + #[test] + fn rejects_invalid_dates() { + assert_eq!(IFC::leap_day(12022), Err(Error::InvalidDate)); + assert_eq!(IFC::ymd(12022, 13, 0), Err(Error::InvalidDate)); + assert_eq!(IFC::ymd(12022, 14, 1), Err(Error::InvalidDate)); + } + + #[test] + fn it_expresses_day_of_week() { + assert_eq!( + IFC::leap_day(12020).unwrap().day_of_week(), + DayOfWeek::LeapDay + ); + assert_eq!(IFC::year_day(12022).day_of_week(), DayOfWeek::YearDay); + assert_eq!( + IFC::ymd(12022, 1, 1).unwrap().day_of_week(), + DayOfWeek::Sunday + ); + assert_eq!( + IFC::ymd(12022, 1, 2).unwrap().day_of_week(), + DayOfWeek::Monday + ); + assert_eq!( + IFC::ymd(12022, 1, 3).unwrap().day_of_week(), + DayOfWeek::Tuesday + ); + assert_eq!( + IFC::ymd(12022, 1, 4).unwrap().day_of_week(), + DayOfWeek::Wednesday + ); + assert_eq!( + IFC::ymd(12022, 1, 5).unwrap().day_of_week(), + DayOfWeek::Thursday + ); + assert_eq!( + IFC::ymd(12022, 1, 6).unwrap().day_of_week(), + DayOfWeek::Friday + ); + assert_eq!( + IFC::ymd(12022, 1, 7).unwrap().day_of_week(), + DayOfWeek::Saturday + ); + assert_eq!( + IFC::ymd(12022, 1, 8).unwrap().day_of_week(), + DayOfWeek::Sunday + ); + } + + #[test] + fn it_reports_ordinal_days() { + assert_eq!(IFC::ymd(12022, 1, 1).unwrap().day_ordinal(), 1); + assert_eq!(IFC::ymd(12022, 1, 1).unwrap().day_ordinal0(), 0); + + assert_eq!(IFC::ymd(12022, 1, 28).unwrap().day_ordinal(), 28); + assert_eq!(IFC::ymd(12022, 1, 28).unwrap().day_ordinal0(), 27); + + assert_eq!(IFC::ymd(12022, 2, 1).unwrap().day_ordinal(), 29); + assert_eq!(IFC::ymd(12022, 2, 1).unwrap().day_ordinal0(), 28); + + assert_eq!(IFC::ymd(12022, 3, 1).unwrap().day_ordinal(), 57); + assert_eq!(IFC::ymd(12022, 3, 1).unwrap().day_ordinal0(), 56); + + assert_eq!(IFC::ymd(12022, 4, 1).unwrap().day_ordinal(), 85); + assert_eq!(IFC::ymd(12022, 4, 1).unwrap().day_ordinal0(), 84); + + assert_eq!(IFC::ymd(12022, 5, 1).unwrap().day_ordinal(), 113); + assert_eq!(IFC::ymd(12022, 5, 1).unwrap().day_ordinal0(), 112); + + assert_eq!(IFC::ymd(12022, 6, 1).unwrap().day_ordinal(), 141); + assert_eq!(IFC::ymd(12022, 6, 1).unwrap().day_ordinal0(), 140); + + assert_eq!(IFC::ymd(12022, 7, 1).unwrap().day_ordinal(), 169); + assert_eq!(IFC::ymd(12022, 7, 1).unwrap().day_ordinal0(), 168); + + assert_eq!(IFC::ymd(12022, 8, 1).unwrap().day_ordinal(), 197); + assert_eq!(IFC::ymd(12022, 8, 1).unwrap().day_ordinal0(), 196); + + assert_eq!(IFC::ymd(12022, 9, 1).unwrap().day_ordinal(), 225); + assert_eq!(IFC::ymd(12022, 9, 1).unwrap().day_ordinal0(), 224); + + assert_eq!(IFC::ymd(12022, 10, 1).unwrap().day_ordinal(), 253); + assert_eq!(IFC::ymd(12022, 10, 1).unwrap().day_ordinal0(), 252); + + assert_eq!(IFC::ymd(12022, 11, 1).unwrap().day_ordinal(), 281); + assert_eq!(IFC::ymd(12022, 11, 1).unwrap().day_ordinal0(), 280); + + assert_eq!(IFC::ymd(12022, 12, 1).unwrap().day_ordinal(), 309); + assert_eq!(IFC::ymd(12022, 12, 1).unwrap().day_ordinal0(), 308); + + assert_eq!(IFC::ymd(12022, 13, 1).unwrap().day_ordinal(), 337); + assert_eq!(IFC::ymd(12022, 13, 1).unwrap().day_ordinal0(), 336); + + assert_eq!(IFC::ymd(12022, 13, 28).unwrap().day_ordinal(), 364); + assert_eq!(IFC::ymd(12022, 13, 28).unwrap().day_ordinal0(), 363); + + assert_eq!(IFC::year_day(12022).day_ordinal(), 365); + } + + #[test] + fn it_reports_ordinal_days_on_leap_year() { + assert_eq!(IFC::ymd(12020, 1, 1).unwrap().day_ordinal(), 1); + assert_eq!(IFC::ymd(12020, 1, 1).unwrap().day_ordinal0(), 0); + + assert_eq!(IFC::ymd(12020, 1, 28).unwrap().day_ordinal(), 28); + assert_eq!(IFC::ymd(12020, 1, 28).unwrap().day_ordinal0(), 27); + + assert_eq!(IFC::ymd(12020, 2, 1).unwrap().day_ordinal(), 29); + assert_eq!(IFC::ymd(12020, 2, 1).unwrap().day_ordinal0(), 28); + + assert_eq!(IFC::ymd(12020, 3, 1).unwrap().day_ordinal(), 57); + assert_eq!(IFC::ymd(12020, 3, 1).unwrap().day_ordinal0(), 56); + + assert_eq!(IFC::ymd(12020, 4, 1).unwrap().day_ordinal(), 85); + assert_eq!(IFC::ymd(12020, 4, 1).unwrap().day_ordinal0(), 84); + + assert_eq!(IFC::ymd(12020, 5, 1).unwrap().day_ordinal(), 113); + assert_eq!(IFC::ymd(12020, 5, 1).unwrap().day_ordinal0(), 112); + + assert_eq!(IFC::ymd(12020, 6, 1).unwrap().day_ordinal(), 141); + assert_eq!(IFC::ymd(12020, 6, 1).unwrap().day_ordinal0(), 140); + + assert_eq!(IFC::leap_day(12020).unwrap().day_ordinal(), 169); + assert_eq!(IFC::leap_day(12020).unwrap().day_ordinal0(), 168); + + assert_eq!(IFC::ymd(12020, 7, 1).unwrap().day_ordinal(), 170); + assert_eq!(IFC::ymd(12020, 7, 1).unwrap().day_ordinal0(), 169); + + assert_eq!(IFC::ymd(12020, 8, 1).unwrap().day_ordinal(), 198); + assert_eq!(IFC::ymd(12020, 8, 1).unwrap().day_ordinal0(), 197); + + assert_eq!(IFC::ymd(12020, 9, 1).unwrap().day_ordinal(), 226); + assert_eq!(IFC::ymd(12020, 9, 1).unwrap().day_ordinal0(), 225); + + assert_eq!(IFC::ymd(12020, 10, 1).unwrap().day_ordinal(), 254); + assert_eq!(IFC::ymd(12020, 10, 1).unwrap().day_ordinal0(), 253); + + assert_eq!(IFC::ymd(12020, 11, 1).unwrap().day_ordinal(), 282); + assert_eq!(IFC::ymd(12020, 11, 1).unwrap().day_ordinal0(), 281); + + assert_eq!(IFC::ymd(12020, 12, 1).unwrap().day_ordinal(), 310); + assert_eq!(IFC::ymd(12020, 12, 1).unwrap().day_ordinal0(), 309); + + assert_eq!(IFC::ymd(12020, 13, 1).unwrap().day_ordinal(), 338); + assert_eq!(IFC::ymd(12020, 13, 1).unwrap().day_ordinal0(), 337); + + assert_eq!(IFC::ymd(12020, 13, 28).unwrap().day_ordinal(), 365); + assert_eq!(IFC::ymd(12020, 13, 28).unwrap().day_ordinal0(), 364); + + assert_eq!(IFC::year_day(12020).day_ordinal(), 366); + } + + #[test] + fn it_reports_ordinal_weeks() { + assert_eq!(IFC::ymd(12022, 1, 1).unwrap().week_ordinal(), 1); + assert_eq!(IFC::ymd(12022, 1, 1).unwrap().week_ordinal0(), 0); + assert_eq!(IFC::ymd(12022, 1, 4).unwrap().week_ordinal(), 1); + assert_eq!(IFC::ymd(12022, 1, 4).unwrap().week_ordinal0(), 0); + assert_eq!(IFC::ymd(12022, 1, 7).unwrap().week_ordinal(), 1); + assert_eq!(IFC::ymd(12022, 1, 7).unwrap().week_ordinal0(), 0); + assert_eq!(IFC::ymd(12022, 1, 8).unwrap().week_ordinal(), 2); + assert_eq!(IFC::ymd(12022, 1, 8).unwrap().week_ordinal0(), 1); + assert_eq!(IFC::ymd(12022, 2, 1).unwrap().week_ordinal(), 5); + assert_eq!(IFC::ymd(12022, 2, 1).unwrap().week_ordinal0(), 4); + assert_eq!(IFC::ymd(12022, 13, 28).unwrap().week_ordinal(), 52); + assert_eq!(IFC::ymd(12022, 13, 28).unwrap().week_ordinal0(), 51); + } + + /* #[test] fn check_start_of_month() { assert_eq!( @@ -391,6 +695,9 @@ mod tests { ); } + #[test] + fn report_leap_day() {} + #[test] fn check_start_of_month_leap_year() { assert_eq!( @@ -663,20 +970,25 @@ mod tests { assert_eq!(IFC::ymd(12019, 13, 29).day(), 29); assert_eq!(IFC::ymd(12019, 13, 29).day0(), 28); } + */ + /* #[test] fn it_reports_correct_day_of_week() { - assert_eq!(IFC::ymd(12019, 1, 1).weekday(), chrono::Weekday::Sun); - assert_eq!(IFC::ymd(12019, 6, 1).weekday(), chrono::Weekday::Sun); - assert_eq!(IFC::ymd(12019, 6, 28).weekday(), chrono::Weekday::Sat); - assert_eq!(IFC::ymd(12019, 7, 1).weekday(), chrono::Weekday::Sun); - assert_eq!(IFC::ymd(12019, 13, 28).weekday(), chrono::Weekday::Sat); - assert_eq!(IFC::ymd(12019, 13, 29).weekday(), chrono::Weekday::Sat); + assert_eq!(IFC::ymd(12019, 1, 1).weekday(), DayOfWeek::Sunday); + assert_eq!(IFC::ymd(12019, 6, 1).weekday(), DayOfWeek::Sunday); + assert_eq!(IFC::ymd(12019, 6, 28).weekday(), DayOfWeek::Saturday); + assert_eq!(IFC::ymd(12019, 7, 1).weekday(), DayOfWeek::Sunday); + assert_eq!(IFC::ymd(12019, 13, 28).weekday(), DayOfWeek::Saturday); + assert_eq!(IFC::ymd(12019, 13, 29).weekday(), DayOfWeek::Saturday); - assert_eq!(IFC::ymd(12020, 6, 28).weekday(), chrono::Weekday::Sat); - assert_eq!(IFC::ymd(12020, 6, 29).weekday(), chrono::Weekday::Sat); - assert_eq!(IFC::ymd(12020, 7, 1).weekday(), chrono::Weekday::Sun); - assert_eq!(IFC::ymd(12020, 13, 28).weekday(), chrono::Weekday::Sat); - assert_eq!(IFC::ymd(12020, 13, 29).weekday(), chrono::Weekday::Sat); + assert_eq!(IFC::ymd(12020, 6, 28).weekday(), DayOfWeek::Saturday); + assert_eq!(IFC::ymd(12020, 6, 29).weekday(), DayOfWeek::Saturday); + assert_eq!(IFC::ymd(12020, 7, 1).weekday(), DayOfWeek::Sunday); + assert_eq!(IFC::ymd(12020, 13, 28).weekday(), DayOfWeek::Saturday); + assert_eq!(IFC::ymd(12020, 13, 29).weekday(), DayOfWeek::Saturday); + + assert_eq!(IFC::ymd(12022, 13, 31) } + */ }