Skip to main content

karbon_framework/validation/constraints/date/
date.rs

1use crate::validation::constraints::{Constraint, ConstraintResult, ConstraintViolation};
2
3/// Validates that a value is a valid date string.
4///
5/// Equivalent to Symfony's `Date` constraint.
6/// Default format: YYYY-MM-DD
7pub struct Date {
8    pub message: String,
9    pub format: String,
10}
11
12impl Default for Date {
13    fn default() -> Self {
14        Self {
15            message: "This value is not a valid date.".to_string(),
16            format: "%Y-%m-%d".to_string(),
17        }
18    }
19}
20
21impl Date {
22    pub fn new() -> Self {
23        Self::default()
24    }
25
26    pub fn with_message(mut self, message: impl Into<String>) -> Self {
27        self.message = message.into();
28        self
29    }
30
31    pub fn with_format(mut self, format: impl Into<String>) -> Self {
32        self.format = format.into();
33        self
34    }
35}
36
37impl Constraint for Date {
38    fn validate(&self, value: &str) -> ConstraintResult {
39        if chrono::NaiveDate::parse_from_str(value, &self.format).is_err() {
40            return Err(ConstraintViolation::new(
41                self.name(),
42                &self.message,
43                value,
44            ));
45        }
46        Ok(())
47    }
48
49    fn name(&self) -> &'static str {
50        "Date"
51    }
52}
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57
58    #[test]
59    fn test_valid_dates() {
60        let constraint = Date::new();
61        assert!(constraint.validate("2024-01-01").is_ok());
62        assert!(constraint.validate("2024-12-31").is_ok());
63        assert!(constraint.validate("2024-02-29").is_ok()); // leap year
64        assert!(constraint.validate("1999-06-15").is_ok());
65    }
66
67    #[test]
68    fn test_invalid_dates() {
69        let constraint = Date::new();
70        assert!(constraint.validate("").is_err());
71        assert!(constraint.validate("not-a-date").is_err());
72        assert!(constraint.validate("2024-13-01").is_err()); // invalid month
73        assert!(constraint.validate("2024-02-30").is_err()); // invalid day
74        assert!(constraint.validate("2023-02-29").is_err()); // not a leap year
75        assert!(constraint.validate("01-01-2024").is_err()); // wrong format
76    }
77
78    #[test]
79    fn test_custom_format() {
80        let constraint = Date::new().with_format("%d/%m/%Y");
81        assert!(constraint.validate("01/01/2024").is_ok());
82        assert!(constraint.validate("31/12/2024").is_ok());
83        assert!(constraint.validate("2024-01-01").is_err());
84    }
85}