use serde::{Deserialize, Serialize};
use crate::error::DurableError;
#[derive(
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Default,
)]
pub struct Duration {
seconds: u64,
}
impl Duration {
pub fn from_seconds(seconds: u64) -> Self {
Self { seconds }
}
pub fn from_minutes(minutes: u64) -> Self {
Self {
seconds: minutes * 60,
}
}
pub fn from_hours(hours: u64) -> Self {
Self {
seconds: hours * 3600,
}
}
pub fn from_days(days: u64) -> Self {
Self {
seconds: days * 86400,
}
}
pub fn from_weeks(weeks: u64) -> Self {
Self {
seconds: weeks * 604800, }
}
pub fn from_months(months: u64) -> Self {
Self {
seconds: months * 2592000, }
}
pub fn from_years(years: u64) -> Self {
Self {
seconds: years * 31536000, }
}
pub fn to_seconds(&self) -> u64 {
self.seconds
}
pub fn validate_for_wait(&self) -> Result<(), DurableError> {
if self.seconds < 1 {
return Err(DurableError::Validation {
message: "Wait duration must be at least 1 second".to_string(),
});
}
Ok(())
}
}
impl From<std::time::Duration> for Duration {
fn from(duration: std::time::Duration) -> Self {
Self {
seconds: duration.as_secs(),
}
}
}
impl From<Duration> for std::time::Duration {
fn from(duration: Duration) -> Self {
std::time::Duration::from_secs(duration.seconds)
}
}
#[cfg(test)]
mod tests {
use super::*;
use proptest::prelude::*;
#[test]
fn test_from_seconds() {
let duration = Duration::from_seconds(42);
assert_eq!(duration.to_seconds(), 42);
}
#[test]
fn test_from_minutes() {
let duration = Duration::from_minutes(5);
assert_eq!(duration.to_seconds(), 300);
}
#[test]
fn test_from_hours() {
let duration = Duration::from_hours(2);
assert_eq!(duration.to_seconds(), 7200);
}
#[test]
fn test_from_days() {
let duration = Duration::from_days(1);
assert_eq!(duration.to_seconds(), 86400);
}
#[test]
fn test_from_weeks() {
let duration = Duration::from_weeks(1);
assert_eq!(duration.to_seconds(), 604800);
}
#[test]
fn test_from_weeks_multiple() {
let duration = Duration::from_weeks(2);
assert_eq!(duration.to_seconds(), 1209600);
}
#[test]
fn test_from_months() {
let duration = Duration::from_months(1);
assert_eq!(duration.to_seconds(), 2592000); }
#[test]
fn test_from_months_multiple() {
let duration = Duration::from_months(3);
assert_eq!(duration.to_seconds(), 7776000); }
#[test]
fn test_from_years() {
let duration = Duration::from_years(1);
assert_eq!(duration.to_seconds(), 31536000); }
#[test]
fn test_from_years_multiple() {
let duration = Duration::from_years(2);
assert_eq!(duration.to_seconds(), 63072000); }
#[test]
fn test_validate_for_wait_valid() {
let duration = Duration::from_seconds(1);
assert!(duration.validate_for_wait().is_ok());
let duration = Duration::from_seconds(100);
assert!(duration.validate_for_wait().is_ok());
}
#[test]
fn test_validate_for_wait_invalid() {
let duration = Duration::from_seconds(0);
assert!(duration.validate_for_wait().is_err());
}
#[test]
fn test_std_duration_conversion() {
let std_duration = std::time::Duration::from_secs(60);
let duration: Duration = std_duration.into();
assert_eq!(duration.to_seconds(), 60);
let back: std::time::Duration = duration.into();
assert_eq!(back.as_secs(), 60);
}
proptest! {
#[test]
fn prop_duration_from_seconds_produces_correct_total(seconds in 0u64..=u64::MAX / 86400) {
let duration = Duration::from_seconds(seconds);
prop_assert_eq!(duration.to_seconds(), seconds);
}
#[test]
fn prop_duration_from_minutes_produces_correct_total(minutes in 0u64..=u64::MAX / 86400 / 60) {
let duration = Duration::from_minutes(minutes);
prop_assert_eq!(duration.to_seconds(), minutes * 60);
}
#[test]
fn prop_duration_from_hours_produces_correct_total(hours in 0u64..=u64::MAX / 86400 / 3600) {
let duration = Duration::from_hours(hours);
prop_assert_eq!(duration.to_seconds(), hours * 3600);
}
#[test]
fn prop_duration_from_days_produces_correct_total(days in 0u64..=u64::MAX / 86400 / 86400) {
let duration = Duration::from_days(days);
prop_assert_eq!(duration.to_seconds(), days * 86400);
}
#[test]
fn prop_duration_from_weeks_produces_correct_total(weeks in 0u64..=u64::MAX / 604800 / 604800) {
let duration = Duration::from_weeks(weeks);
prop_assert_eq!(duration.to_seconds(), weeks * 604800);
}
#[test]
fn prop_duration_from_months_produces_correct_total(months in 0u64..=u64::MAX / 2592000 / 2592000) {
let duration = Duration::from_months(months);
prop_assert_eq!(duration.to_seconds(), months * 2592000);
}
#[test]
fn prop_duration_from_years_produces_correct_total(years in 0u64..=u64::MAX / 31536000 / 31536000) {
let duration = Duration::from_years(years);
prop_assert_eq!(duration.to_seconds(), years * 31536000);
}
#[test]
fn prop_duration_validate_for_wait_rejects_zero(seconds in 0u64..1) {
let duration = Duration::from_seconds(seconds);
prop_assert!(duration.validate_for_wait().is_err());
}
#[test]
fn prop_duration_validate_for_wait_accepts_valid(seconds in 1u64..=1_000_000) {
let duration = Duration::from_seconds(seconds);
prop_assert!(duration.validate_for_wait().is_ok());
}
#[test]
fn prop_duration_std_roundtrip(seconds in 0u64..=u64::MAX / 2) {
let duration = Duration::from_seconds(seconds);
let std_duration: std::time::Duration = duration.into();
let back: Duration = std_duration.into();
prop_assert_eq!(duration, back);
}
}
}