From ca5a8312e49a8918187eeb7afb7fab511adb5685 Mon Sep 17 00:00:00 2001 From: Steppy Date: Tue, 18 Feb 2025 17:11:07 +0100 Subject: [PATCH] Migrate leptos_webpage to session_iter logic (build now broken) --- leptos_webpage/Cargo.toml | 1 - leptos_webpage/src/bin/cli.rs | 29 --- .../src/bin/create_default_configs.rs | 6 +- leptos_webpage/src/lib.rs | 8 +- leptos_webpage/src/session.rs | 243 ------------------ leptos_webpage/src/session_date_calculator.rs | 140 ---------- leptos_webpage/src/webpage.rs | 53 ++-- session_iter/src/session.rs | 2 + 8 files changed, 31 insertions(+), 451 deletions(-) delete mode 100644 leptos_webpage/src/bin/cli.rs delete mode 100644 leptos_webpage/src/session.rs delete mode 100644 leptos_webpage/src/session_date_calculator.rs diff --git a/leptos_webpage/Cargo.toml b/leptos_webpage/Cargo.toml index 255443c..82b0afa 100644 --- a/leptos_webpage/Cargo.toml +++ b/leptos_webpage/Cargo.toml @@ -13,5 +13,4 @@ leptos = { version = "0.7", features = ["csr"] } console_error_panic_hook = "0.1.7" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -clap = { version = "4.5", features = ["derive"] } session_iter = { path = "../session_iter"} \ No newline at end of file diff --git a/leptos_webpage/src/bin/cli.rs b/leptos_webpage/src/bin/cli.rs deleted file mode 100644 index 8a3101d..0000000 --- a/leptos_webpage/src/bin/cli.rs +++ /dev/null @@ -1,29 +0,0 @@ -use clap::Parser; -use jana_sessions_webpage::localize_day; -use jana_sessions_webpage::session_date_calculator::{DayIter, NthWeekday}; -use std::io::stdin; - -#[derive(Debug, Parser)] -struct Args { - sessions: Vec, -} - -fn main() { - let args = Args::parse(); - - let mut iter = DayIter::default().filter(|day| { - args - .sessions - .iter() - .any(|nth_weekday| nth_weekday.matches(day)) - }); - - loop { - iter - .by_ref() - .take(3) - .for_each(|day| println!("{}", localize_day(&day))); - println!("Press enter for more..."); - stdin().read_line(&mut String::new()).unwrap(); - } -} diff --git a/leptos_webpage/src/bin/create_default_configs.rs b/leptos_webpage/src/bin/create_default_configs.rs index dff3530..c260e91 100644 --- a/leptos_webpage/src/bin/create_default_configs.rs +++ b/leptos_webpage/src/bin/create_default_configs.rs @@ -4,11 +4,11 @@ use std::fs::File; use std::path::Path; use std::{env, fs, io}; -fn main() -> io::Result<()> { +fn main() -> Result<(), String> { let out_dir = env::var_os("TRUNK_STAGING_DIR").unwrap_or("target/default_configs".into()); - fs::create_dir_all(&out_dir)?; + fs::create_dir_all(&out_dir).map_err(|e| format!("failed to create target directory: {e}"))?; - create_default_config::("session_config", &out_dir)?; + create_default_config::("session_config", &out_dir).map_err(|e| format!("Failed to create session_config: {e}"))?; Ok(()) } diff --git a/leptos_webpage/src/lib.rs b/leptos_webpage/src/lib.rs index f770fa1..e0f6f66 100644 --- a/leptos_webpage/src/lib.rs +++ b/leptos_webpage/src/lib.rs @@ -1,14 +1,12 @@ -use crate::session_date_calculator::Day; use chrono::Weekday; +use session_iter::day::Day; -pub mod session; -pub mod session_date_calculator; pub mod webpage; pub fn localize_day(day: &Day) -> String { format!( "{}, {}", - match day.weekday { + match day.weekday() { Weekday::Mon => "Montag", Weekday::Tue => "Dienstag", Weekday::Wed => "Mittwoch", @@ -17,6 +15,6 @@ pub fn localize_day(day: &Day) -> String { Weekday::Sat => "Samstag", Weekday::Sun => "Sonntag", }, - day.date.format("%d.%m.%Y") + day.date().format("%d.%m.%Y") ) } diff --git a/leptos_webpage/src/session.rs b/leptos_webpage/src/session.rs deleted file mode 100644 index 1f86678..0000000 --- a/leptos_webpage/src/session.rs +++ /dev/null @@ -1,243 +0,0 @@ -use crate::session_date_calculator::{Day, NthWeekday}; -use chrono::NaiveDate; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; - -#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] -pub enum DatedSession { - Regular { - session: RegularSession, - day: Day, - }, - Extra(ExtraSession), - Cancelled { - session: RegularSession, - day: Day, - exception: CancelException, - }, - Altered { - session: RegularSession, - day: Day, - exception: AlterException, - }, -} - -impl DatedSession { - pub fn date(&self) -> &NaiveDate { - match self { - DatedSession::Regular { day, .. } => &day.date, - DatedSession::Extra(ExtraSession { date, .. }) => date, - DatedSession::Cancelled { day, .. } => &day.date, - DatedSession::Altered { day, .. } => &day.date, - } - } -} - -impl Noted for DatedSession { - fn get_note(&self) -> Option<&String> { - match self { - DatedSession::Regular { session, .. } => session.get_note(), - DatedSession::Extra(session) => session.get_note(), - DatedSession::Cancelled { exception, .. } => exception.get_note(), - DatedSession::Altered { exception, .. } => exception.get_note(), - } - } -} - -#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] -pub enum Session { - Regular(RegularSession), - Extra(ExtraSession), -} - -impl From for Session { - fn from(value: RegularSession) -> Self { - Self::Regular(value) - } -} - -impl From for Session { - fn from(value: ExtraSession) -> Self { - Self::Extra(value) - } -} - -impl Session { - pub fn into_dated(self, day: Day) -> Result { - if !self.is_applicable_to(&day) { - return Err(self); - } - - let dated_session = match self { - Session::Regular(session) => match session.except.get(&day.date) { - Some(exception) => match exception.clone() { - Exception::Cancel(exception) => DatedSession::Cancelled { - session, - day, - exception, - }, - Exception::Alter(exception) => DatedSession::Altered { - session, - day, - exception, - }, - }, - None => DatedSession::Regular { session, day }, - }, - Session::Extra(session) => DatedSession::Extra(session), - }; - Ok(dated_session) - } - - pub fn is_applicable_to(&self, day: &Day) -> bool { - match *self { - Session::Regular(RegularSession { rule, .. }) => rule.matches(day), - Session::Extra(ExtraSession { date, .. }) => day.date == date, - } - } -} - -impl Noted for Session { - fn get_note(&self) -> Option<&String> { - match self { - Session::Regular(session) => session.get_note(), - Session::Extra(session) => session.get_note(), - } - } -} - -#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] -pub struct RegularSession { - pub rule: NthWeekday, - pub note: Option, - pub except: HashMap, -} - -impl From for RegularSession { - fn from(rule: NthWeekday) -> Self { - Self { - rule, - note: None, - except: Default::default(), - } - } -} - -impl RegularSession { - pub fn with_exception(mut self, date: D, exception: E) -> Self - where - D: Into, - E: Into, - { - self.except.insert(date.into(), exception.into()); - self - } -} - -#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] -pub struct ExtraSession { - pub date: NaiveDate, - pub note: Option, -} - -impl From for ExtraSession { - fn from(date: NaiveDate) -> Self { - Self { date, note: None } - } -} - -#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] -pub enum Exception { - Cancel(CancelException), - Alter(AlterException), -} - -impl From for Exception { - fn from(value: CancelException) -> Self { - Self::Cancel(value) - } -} - -impl From for Exception { - fn from(value: AlterException) -> Self { - Self::Alter(value) - } -} - -impl Noted for Exception { - fn get_note(&self) -> Option<&String> { - match self { - Exception::Cancel(exception) => exception.get_note(), - Exception::Alter(exception) => exception.get_note(), - } - } -} - -#[derive(Debug, Clone, Eq, PartialEq, Hash, Default, Serialize, Deserialize)] -#[serde(default)] -pub struct CancelException { - pub note: Option, -} - -#[derive(Debug, Clone, Eq, PartialEq, Hash, Default, Serialize, Deserialize)] -#[serde(default)] -pub struct AlterException { - pub date: Option, - pub note: Option, -} - -impl From for AlterException { - fn from(value: NaiveDate) -> Self { - Self { - date: Some(value), - ..Self::default() - } - } -} - -pub trait Noted { - fn get_note(&self) -> Option<&String>; -} - -macro_rules! impl_noted { - ($ty: ident) => { - impl Noted for $ty { - fn get_note(&self) -> Option<&String> { - self.note.as_ref() - } - } - }; -} - -impl_noted!(RegularSession); -impl_noted!(ExtraSession); -impl_noted!(AlterException); -impl_noted!(CancelException); - -pub trait WithNote { - fn with_note(self, note: S) -> Self - where - S: ToString; -} - -macro_rules! impl_with_node { - ($ty: ident) => { - impl WithNote for $ty { - fn with_note(self, note: S) -> Self - where - S: ToString, - { - #[allow(clippy::needless_update)] - Self { - note: Some(note.to_string()), - ..self - } - } - } - }; -} - -impl_with_node!(AlterException); -impl_with_node!(CancelException); -impl_with_node!(ExtraSession); -impl_with_node!(RegularSession); diff --git a/leptos_webpage/src/session_date_calculator.rs b/leptos_webpage/src/session_date_calculator.rs deleted file mode 100644 index 0ed3a5b..0000000 --- a/leptos_webpage/src/session_date_calculator.rs +++ /dev/null @@ -1,140 +0,0 @@ -use chrono::{Datelike, Days, Local, NaiveDate, ParseWeekdayError, Weekday}; -use serde::{Deserialize, Serialize}; -use std::error::Error; -use std::fmt::{Display, Formatter}; -use std::num::ParseIntError; -use std::str::FromStr; - -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] -pub struct NthWeekday { - pub n: u8, - pub weekday: Weekday, -} - -impl NthWeekday { - pub fn new(n: u8, weekday: Weekday) -> Self { - Self { n, weekday } - } - - pub fn matches(&self, day: &Day) -> bool { - self.weekday == day.weekday && self.n == day.week_of_month - } -} - -impl FromStr for NthWeekday { - type Err = NthWeekdayParseError; - - fn from_str(s: &str) -> Result { - let (number, day) = s - .split_once(' ') - .ok_or(NthWeekdayParseError::InvalidFormat)?; - let weekday = Weekday::from_str(day) - .map_err(|e| NthWeekdayParseError::InvalidWeekday(day.to_string(), e))?; - let number = number - .trim_end_matches(|c: char| !c.is_ascii_digit()) - .parse::() - .map_err(|e| NthWeekdayParseError::InvalidN(number.to_string(), e))?; - Ok(Self::new(number, weekday)) - } -} - -#[derive(Debug)] -pub enum NthWeekdayParseError { - InvalidFormat, - InvalidWeekday(String, ParseWeekdayError), - InvalidN(String, ParseIntError), -} - -impl Display for NthWeekdayParseError { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - NthWeekdayParseError::InvalidFormat => write!(f, "Invalid format, use e.g '3rd fri'"), - NthWeekdayParseError::InvalidWeekday(week_day, e) => { - write!(f, "Invalid weekday '{week_day}': {e}") - } - NthWeekdayParseError::InvalidN(number, e) => write!(f, "Invalid number '{number}': {e}"), - } - } -} - -impl Error for NthWeekdayParseError {} - -#[derive(Debug)] -pub struct DayIter { - date: Option, -} - -impl From for DayIter { - fn from(value: NaiveDate) -> Self { - Self { date: value.into() } - } -} - -impl From for DayIter { - fn from(value: Day) -> Self { - Self { - date: value.date.into(), - } - } -} - -impl Default for DayIter { - fn default() -> Self { - Self { - date: today().into(), - } - } -} - -impl Iterator for DayIter { - type Item = Day; - - fn next(&mut self) -> Option { - let date = self.date; - self.date = date.and_then(|date| date.checked_add_days(Days::new(1))); - date.map(Day::from) - } -} - -#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] -pub struct Day { - pub date: NaiveDate, - pub weekday: Weekday, - pub week_of_month: u8, -} - -impl From for Day { - fn from(date: NaiveDate) -> Self { - Self { - date, - weekday: date.weekday(), - week_of_month: date.day0() as u8 / 7 + 1, - } - } -} - -impl Default for Day { - fn default() -> Self { - Self::from(today()) - } -} - -fn today() -> NaiveDate { - Local::now().date_naive() -} - -#[cfg(test)] -mod test { - use crate::session_date_calculator::DayIter; - use chrono::NaiveDate; - - #[test] - fn test_day_iter() { - let mut day_iter = DayIter::from(NaiveDate::from_ymd_opt(2025, 2, 1).expect("valid date")); - for week_of_month in 1..=4 { - for _ in 0..7 { - assert_eq!(day_iter.next().unwrap().week_of_month, week_of_month) - } - } - } -} diff --git a/leptos_webpage/src/webpage.rs b/leptos_webpage/src/webpage.rs index e8f54cf..7e27e2c 100644 --- a/leptos_webpage/src/webpage.rs +++ b/leptos_webpage/src/webpage.rs @@ -1,12 +1,12 @@ use crate::localize_day; -use crate::session::{ - AlterException, CancelException, DatedSession, ExtraSession, RegularSession, Session, WithNote, -}; -use crate::session_date_calculator::{Day, DayIter, NthWeekday}; use chrono::{Datelike, Days, Months, NaiveDate, Weekday}; use leptos::prelude::*; use leptos::server_fn::request::browser::Request; use serde::{Deserialize, Serialize}; +use session_iter::day::Day; +use session_iter::session::iter::{DatedSession, DatedSessionIter}; +use session_iter::session::rule::WeekdayOfMonth; +use session_iter::session::{Alternation, Cancellation, Dated, ExtraSession, RegularSession, Session, WithNote}; #[component] pub fn App() -> impl IntoView { @@ -42,13 +42,7 @@ pub fn App() -> impl IntoView { #[component] fn Sessions(config: SessionConfig) -> impl IntoView { - let mut session_iter = DayIter::default().flat_map(move |day| { - config - .sessions - .clone() - .into_iter() - .filter_map(move |session| session.clone().into_dated(day.clone()).ok()) - }); + let mut session_iter = DatedSessionIter::new(Day::default(), config.sessions); let (dated_sessions, mut_dated_sessions) = signal(session_iter.by_ref().take(2).collect::>()); @@ -60,7 +54,7 @@ fn Sessions(config: SessionConfig) -> impl IntoView { @@ -93,26 +87,26 @@ fn DatedSession(session: DatedSession) -> impl IntoView { DatedSession::Extra(session) => view! {

""

-

{localize_day(&session.date.into())}

+

{localize_day(&session.day)}

{session.note}

} .into_any(), - DatedSession::Cancelled { session, day, .. } => view! { + DatedSession::Canceled { regular, day, .. } => view! {

""

{localize_day(&day)}

-

{session.note}

+

{regular.note}

} .into_any(), DatedSession::Altered { - session, + regular, day, - exception, + cause, .. } => { - let day = move || match exception.date.map(Day::from) { + let day = move || match cause.new_day { None => view! { {localize_day(&day)} }.into_any(), Some(new_day) => view! { {localize_day(&day)} @@ -126,7 +120,7 @@ fn DatedSession(session: DatedSession) -> impl IntoView {

"<Änderung>"

{day}

-

{exception.note.or(session.note)}

+

{cause.note.or(regular.note)}

} .into_any() @@ -171,11 +165,10 @@ macro_rules! date { } impl Default for SessionConfig { - #[allow(clippy::zero_prefixed_literal)] fn default() -> Self { const YEAR: i32 = 2024; - const NEXT_TUE_SESSION: NaiveDate = date!(18, 02, YEAR); - const NEXT_SUN_SESSION: NaiveDate = date!(02, 03, YEAR); + const NEXT_TUE_SESSION: NaiveDate = date!(18, 2, YEAR); + const NEXT_SUN_SESSION: NaiveDate = date!(2, 3, YEAR); let next_next_sun_session = { let some_day_next_month = NEXT_SUN_SESSION.checked_add_months(Months::new(1)).unwrap(); NaiveDate::from_weekday_of_month_opt( @@ -190,26 +183,26 @@ impl Default for SessionConfig { Self { motd: Some("Jeder 1. Sonntag und 3. Dienstag im Monat".into()), sessions: vec![ - RegularSession::from(NthWeekday::new(1, Weekday::Sun)) + RegularSession::from(WeekdayOfMonth::new(1, Weekday::Sun)) .with_note("10:00 Uhr") - .with_exception( + .except( NEXT_SUN_SESSION, - AlterException { - date: Some(NEXT_SUN_SESSION.checked_sub_days(Days::new(1)).unwrap()), + Alternation { + new_day: Some(NEXT_SUN_SESSION.checked_sub_days(Days::new(1)).unwrap().into()), ..Default::default() }, ) - .with_exception( + .except( next_next_sun_session, - AlterException { + Alternation { note: Some("11 Uhr".into()), ..Default::default() }, ) .into(), - RegularSession::from(NthWeekday::new(3, Weekday::Tue)) + RegularSession::from(WeekdayOfMonth::new(3, Weekday::Tue)) .with_note("18:30 Uhr") - .with_exception(NEXT_TUE_SESSION, CancelException::default()) + .except(NEXT_TUE_SESSION, Cancellation::new()) .into(), ExtraSession::from(NEXT_TUE_SESSION.checked_add_days(Days::new(2)).unwrap()) .with_note("18 Uhr") diff --git a/session_iter/src/session.rs b/session_iter/src/session.rs index 9c245a6..e9ba002 100644 --- a/session_iter/src/session.rs +++ b/session_iter/src/session.rs @@ -78,6 +78,8 @@ pub struct RegularSession { pub except: BTreeMap, } +//TODO we need to implement serialize ourselves, since json doesn't support anything other than string keys + impl From for RegularSession { fn from(rule: SessionRule) -> Self { Self {