Migrate leptos_webpage to session_iter logic (build now broken)
This commit is contained in:
parent
6208e5d1dc
commit
ca5a8312e4
@ -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"}
|
||||
@ -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<NthWeekday>,
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
@ -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::<SessionConfig, _>("session_config", &out_dir)?;
|
||||
create_default_config::<SessionConfig, _>("session_config", &out_dir).map_err(|e| format!("Failed to create session_config: {e}"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@ -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")
|
||||
)
|
||||
}
|
||||
|
||||
@ -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<RegularSession> for Session {
|
||||
fn from(value: RegularSession) -> Self {
|
||||
Self::Regular(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ExtraSession> for Session {
|
||||
fn from(value: ExtraSession) -> Self {
|
||||
Self::Extra(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Session {
|
||||
pub fn into_dated(self, day: Day) -> Result<DatedSession, Self> {
|
||||
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<String>,
|
||||
pub except: HashMap<NaiveDate, Exception>,
|
||||
}
|
||||
|
||||
impl From<NthWeekday> for RegularSession {
|
||||
fn from(rule: NthWeekday) -> Self {
|
||||
Self {
|
||||
rule,
|
||||
note: None,
|
||||
except: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RegularSession {
|
||||
pub fn with_exception<D, E>(mut self, date: D, exception: E) -> Self
|
||||
where
|
||||
D: Into<NaiveDate>,
|
||||
E: Into<Exception>,
|
||||
{
|
||||
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<String>,
|
||||
}
|
||||
|
||||
impl From<NaiveDate> 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<CancelException> for Exception {
|
||||
fn from(value: CancelException) -> Self {
|
||||
Self::Cancel(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AlterException> 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<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, Default, Serialize, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct AlterException {
|
||||
pub date: Option<NaiveDate>,
|
||||
pub note: Option<String>,
|
||||
}
|
||||
|
||||
impl From<NaiveDate> 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<S>(self, note: S) -> Self
|
||||
where
|
||||
S: ToString;
|
||||
}
|
||||
|
||||
macro_rules! impl_with_node {
|
||||
($ty: ident) => {
|
||||
impl WithNote for $ty {
|
||||
fn with_note<S>(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);
|
||||
@ -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<Self, Self::Err> {
|
||||
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::<u8>()
|
||||
.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<NaiveDate>,
|
||||
}
|
||||
|
||||
impl From<NaiveDate> for DayIter {
|
||||
fn from(value: NaiveDate) -> Self {
|
||||
Self { date: value.into() }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Day> 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<Self::Item> {
|
||||
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<NaiveDate> 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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::<Vec<_>>());
|
||||
|
||||
@ -60,7 +54,7 @@ fn Sessions(config: SessionConfig) -> impl IntoView {
|
||||
</div>
|
||||
<For
|
||||
each=move || dated_sessions.get()
|
||||
key=|session| *session.date()
|
||||
key=|session| session.day()
|
||||
children=move |session| {
|
||||
view! {
|
||||
<DatedSession session />
|
||||
@ -93,26 +87,26 @@ fn DatedSession(session: DatedSession) -> impl IntoView {
|
||||
DatedSession::Extra(session) => view! {
|
||||
<div class="box highlight-background">
|
||||
<p class="small-text">"<Extra Probe>"</p>
|
||||
<p><b>{localize_day(&session.date.into())}</b></p>
|
||||
<p><b>{localize_day(&session.day)}</b></p>
|
||||
<p>{session.note}</p>
|
||||
</div>
|
||||
}
|
||||
.into_any(),
|
||||
DatedSession::Cancelled { session, day, .. } => view! {
|
||||
DatedSession::Canceled { regular, day, .. } => view! {
|
||||
<div class="box cancel-background">
|
||||
<p class="small-text">"<Entfall>"</p>
|
||||
<p class="strikethrough bold">{localize_day(&day)}</p>
|
||||
<p>{session.note}</p>
|
||||
<p>{regular.note}</p>
|
||||
</div>
|
||||
}
|
||||
.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! {
|
||||
<span class="strikethrough">{localize_day(&day)}</span>
|
||||
@ -126,7 +120,7 @@ fn DatedSession(session: DatedSession) -> impl IntoView {
|
||||
<div class="box change-background">
|
||||
<p class="small-text">"<Änderung>"</p>
|
||||
<p class="bold">{day}</p>
|
||||
<p>{exception.note.or(session.note)}</p>
|
||||
<p>{cause.note.or(regular.note)}</p>
|
||||
</div>
|
||||
}
|
||||
.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")
|
||||
|
||||
@ -78,6 +78,8 @@ pub struct RegularSession {
|
||||
pub except: BTreeMap<Day, Except>,
|
||||
}
|
||||
|
||||
//TODO we need to implement serialize ourselves, since json doesn't support anything other than string keys
|
||||
|
||||
impl From<SessionRule> for RegularSession {
|
||||
fn from(rule: SessionRule) -> Self {
|
||||
Self {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user