diff --git a/crates/adapters/postgres/src/wrapup.rs b/crates/adapters/postgres/src/wrapup.rs index 445ac15..470c58f 100644 --- a/crates/adapters/postgres/src/wrapup.rs +++ b/crates/adapters/postgres/src/wrapup.rs @@ -282,7 +282,7 @@ impl WrapUpStatsQuery for PostgresWrapUpStatsQuery { ORDER BY r.watched_at ASC" ); - let mut q = sqlx::query(&sql).bind(range.start).bind(range.end); + let mut q = sqlx::query(&sql).bind(range.start()).bind(range.end()); if let Some(ref uid) = scope_bind { q = q.bind(uid); } diff --git a/crates/adapters/sqlite/src/wrapup.rs b/crates/adapters/sqlite/src/wrapup.rs index 3f466e4..a4101dc 100644 --- a/crates/adapters/sqlite/src/wrapup.rs +++ b/crates/adapters/sqlite/src/wrapup.rs @@ -274,8 +274,8 @@ impl WrapUpStatsQuery for SqliteWrapUpStatsQuery { scope: &WrapUpScope, range: &DateRange, ) -> Result, DomainError> { - let start_str = range.start.format("%Y-%m-%d").to_string(); - let end_str = range.end.format("%Y-%m-%d").to_string(); + let start_str = range.start().format("%Y-%m-%d").to_string(); + let end_str = range.end().format("%Y-%m-%d").to_string(); // 1) Main query let (scope_clause, scope_bind) = match scope { diff --git a/crates/adapters/wrapup-renderer/src/slides.rs b/crates/adapters/wrapup-renderer/src/slides.rs index 4ac59d7..bc0c28d 100644 --- a/crates/adapters/wrapup-renderer/src/slides.rs +++ b/crates/adapters/wrapup-renderer/src/slides.rs @@ -277,8 +277,8 @@ impl SlideRenderer { let year_label = format!( "{} - {}", - report.date_range.start.format("%b %Y"), - report.date_range.end.format("%b %Y") + report.date_range.start().format("%b %Y"), + report.date_range.end().format("%b %Y") ); self.draw_centered(&mut img, &year_label, (h / 6) as i32, 48.0, DIM); self.draw_centered( diff --git a/crates/application/src/wrapup/generate.rs b/crates/application/src/wrapup/generate.rs index d89cb9e..d830b72 100644 --- a/crates/application/src/wrapup/generate.rs +++ b/crates/application/src/wrapup/generate.rs @@ -1,24 +1,15 @@ use chrono::Utc; use domain::errors::DomainError; use domain::events::DomainEvent; -use domain::models::wrapup::WrapUpStatus; +use domain::models::wrapup::{DateRange, WrapUpStatus}; use domain::value_objects::{UserId, WrapUpId}; use crate::context::AppContext; use crate::wrapup::commands::RequestWrapUpCommand; pub async fn execute(ctx: &AppContext, cmd: RequestWrapUpCommand) -> Result { - if cmd.end_date <= cmd.start_date { - return Err(DomainError::ValidationError( - "end_date must be after start_date".into(), - )); - } - let days = (cmd.end_date - cmd.start_date).num_days(); - if days > 366 { - return Err(DomainError::ValidationError( - "date range cannot exceed 366 days".into(), - )); - } + let date_range = DateRange::new(cmd.start_date, cmd.end_date)?; + if cmd.end_date > Utc::now().date_naive() { return Err(DomainError::ValidationError( "end_date cannot be in the future".into(), @@ -28,7 +19,7 @@ pub async fn execute(ctx: &AppContext, cmd: RequestWrapUpCommand) -> Result Result Result WrapUpMovieRow { } fn year_2024_range() -> DateRange { - DateRange { - start: NaiveDate::from_ymd_opt(2024, 1, 1).unwrap(), - end: NaiveDate::from_ymd_opt(2025, 1, 1).unwrap(), - } + DateRange::new( + NaiveDate::from_ymd_opt(2024, 1, 1).unwrap(), + NaiveDate::from_ymd_opt(2025, 1, 1).unwrap(), + ) + .unwrap() } #[tokio::test] diff --git a/crates/domain/src/models/wrapup.rs b/crates/domain/src/models/wrapup.rs index fe07dcd..8752221 100644 --- a/crates/domain/src/models/wrapup.rs +++ b/crates/domain/src/models/wrapup.rs @@ -6,8 +6,32 @@ use crate::value_objects::WrapUpId; #[derive(Clone, Debug, Serialize, Deserialize)] pub struct DateRange { - pub start: NaiveDate, - pub end: NaiveDate, + start: NaiveDate, + end: NaiveDate, +} + +impl DateRange { + pub fn new(start: NaiveDate, end: NaiveDate) -> Result { + if end <= start { + return Err(crate::errors::DomainError::ValidationError( + "end_date must be after start_date".into(), + )); + } + if (end - start).num_days() > 366 { + return Err(crate::errors::DomainError::ValidationError( + "date range cannot exceed 366 days".into(), + )); + } + Ok(Self { start, end }) + } + + pub fn start(&self) -> NaiveDate { + self.start + } + + pub fn end(&self) -> NaiveDate { + self.end + } } #[derive(Clone, Debug, Serialize, Deserialize)] diff --git a/crates/domain/src/testing.rs b/crates/domain/src/testing.rs index cd876dc..54f28ee 100644 --- a/crates/domain/src/testing.rs +++ b/crates/domain/src/testing.rs @@ -1047,7 +1047,7 @@ impl crate::ports::WrapUpStatsQuery for InMemoryWrapUpStatsQuery { .iter() .filter(|r| { let date = r.watched_at.date(); - date >= range.start && date < range.end + date >= range.start() && date < range.end() }) .filter(|r| match scope { crate::models::wrapup::WrapUpScope::User(uid) => r.user_id == *uid,