karbon_framework/validation/constraints/number/
range.rs1use crate::validation::constraints::{ConstraintResult, ConstraintViolation, NumericConstraint};
2
3pub struct Range {
7 pub min: Option<f64>,
8 pub max: Option<f64>,
9 pub min_message: String,
10 pub max_message: String,
11}
12
13impl Default for Range {
14 fn default() -> Self {
15 Self {
16 min: None,
17 max: None,
18 min_message: "This value should be {{ limit }} or more.".to_string(),
19 max_message: "This value should be {{ limit }} or less.".to_string(),
20 }
21 }
22}
23
24impl Range {
25 pub fn new() -> Self {
26 Self::default()
27 }
28
29 pub fn min(mut self, min: f64) -> Self {
30 self.min = Some(min);
31 self
32 }
33
34 pub fn max(mut self, max: f64) -> Self {
35 self.max = Some(max);
36 self
37 }
38
39 pub fn between(min: f64, max: f64) -> Self {
40 Self {
41 min: Some(min),
42 max: Some(max),
43 ..Self::default()
44 }
45 }
46
47 pub fn with_min_message(mut self, message: impl Into<String>) -> Self {
48 self.min_message = message.into();
49 self
50 }
51
52 pub fn with_max_message(mut self, message: impl Into<String>) -> Self {
53 self.max_message = message.into();
54 self
55 }
56
57 fn format_message(template: &str, limit: f64) -> String {
58 template.replace("{{ limit }}", &limit.to_string())
59 }
60}
61
62impl NumericConstraint for Range {
63 fn validate_f64(&self, value: f64) -> ConstraintResult {
64 if let Some(min) = self.min {
65 if value < min {
66 return Err(ConstraintViolation::new(
67 self.name(),
68 Self::format_message(&self.min_message, min),
69 value.to_string(),
70 ));
71 }
72 }
73
74 if let Some(max) = self.max {
75 if value > max {
76 return Err(ConstraintViolation::new(
77 self.name(),
78 Self::format_message(&self.max_message, max),
79 value.to_string(),
80 ));
81 }
82 }
83
84 Ok(())
85 }
86
87 fn name(&self) -> &'static str {
88 "Range"
89 }
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95
96 #[test]
97 fn test_within_range() {
98 let constraint = Range::between(1.0, 100.0);
99 assert!(constraint.validate_f64(1.0).is_ok());
100 assert!(constraint.validate_f64(50.0).is_ok());
101 assert!(constraint.validate_f64(100.0).is_ok());
102 }
103
104 #[test]
105 fn test_out_of_range() {
106 let constraint = Range::between(1.0, 100.0);
107 assert!(constraint.validate_f64(0.0).is_err());
108 assert!(constraint.validate_f64(101.0).is_err());
109 assert!(constraint.validate_f64(-5.0).is_err());
110 }
111
112 #[test]
113 fn test_min_only() {
114 let constraint = Range::new().min(0.0);
115 assert!(constraint.validate_f64(0.0).is_ok());
116 assert!(constraint.validate_f64(1000.0).is_ok());
117 assert!(constraint.validate_f64(-1.0).is_err());
118 }
119
120 #[test]
121 fn test_max_only() {
122 let constraint = Range::new().max(100.0);
123 assert!(constraint.validate_f64(-1000.0).is_ok());
124 assert!(constraint.validate_f64(100.0).is_ok());
125 assert!(constraint.validate_f64(101.0).is_err());
126 }
127
128 #[test]
129 fn test_float_values() {
130 let constraint = Range::between(0.0, 1.0);
131 assert!(constraint.validate_f64(0.5).is_ok());
132 assert!(constraint.validate_f64(0.99).is_ok());
133 assert!(constraint.validate_f64(1.01).is_err());
134 }
135}