use crate::domain::errors::DomainError;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct TakenAt {
year: i32,
month: u8,
day: u8,
hour: u8,
minute: u8,
}
impl TakenAt {
pub fn new(
year: i32,
month: u8,
day: u8,
hour: u8,
minute: u8,
now: (i32, u8, u8, u8, u8),
) -> Result<Self, DomainError> {
if hour > 23 || minute > 59 {
return Err(DomainError::InvalidTakenAt);
}
if (year, month, day, hour, minute) > now {
return Err(DomainError::TakenAtInFuture);
}
Ok(Self {
year,
month,
day,
hour,
minute,
})
}
pub fn year(&self) -> i32 {
self.year
}
pub fn month(&self) -> u8 {
self.month
}
pub fn day(&self) -> u8 {
self.day
}
pub fn hour(&self) -> u8 {
self.hour
}
pub fn minute(&self) -> u8 {
self.minute
}
}
impl std::fmt::Display for TakenAt {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{:04}-{:02}-{:02} {:02}:{:02}",
self.year, self.month, self.day, self.hour, self.minute
)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn now() -> (i32, u8, u8, u8, u8) {
(2025, 6, 1, 8, 30)
}
#[test]
fn new_with_past_datetime_returns_taken_at() {
let result = TakenAt::new(2025, 6, 1, 8, 0, now());
assert!(result.is_ok());
}
#[test]
fn new_with_same_minute_as_now_returns_taken_at() {
let result = TakenAt::new(2025, 6, 1, 8, 30, now());
assert!(result.is_ok());
}
#[test]
fn new_with_future_datetime_returns_error() {
let result = TakenAt::new(2025, 6, 1, 9, 0, now());
assert!(matches!(result, Err(DomainError::TakenAtInFuture)));
}
#[test]
fn new_with_invalid_hour_returns_error() {
let result = TakenAt::new(2025, 6, 1, 24, 0, now());
assert!(matches!(result, Err(DomainError::InvalidTakenAt)));
}
#[test]
fn new_with_invalid_minute_returns_error() {
let result = TakenAt::new(2025, 6, 1, 8, 60, now());
assert!(matches!(result, Err(DomainError::InvalidTakenAt)));
}
#[test]
fn accessors_return_stored_components() {
let taken = TakenAt::new(2025, 6, 1, 8, 0, now()).unwrap();
assert_eq!(taken.year(), 2025);
assert_eq!(taken.month(), 6);
assert_eq!(taken.day(), 1);
assert_eq!(taken.hour(), 8);
assert_eq!(taken.minute(), 0);
}
#[test]
fn display_formats_as_date_and_hh_mm() {
let taken = TakenAt::new(2025, 6, 1, 20, 30, (2025, 6, 1, 20, 35)).unwrap();
assert_eq!(taken.to_string(), "2025-06-01 20:30");
}
#[test]
fn earlier_datetime_is_less_than_later_datetime() {
let morning = TakenAt::new(2025, 6, 1, 8, 0, now()).unwrap();
let evening = TakenAt::new(2025, 6, 1, 8, 30, now()).unwrap();
assert!(morning < evening);
}
}