karbon_framework/validation/constraints/collection/
count.rs1use crate::validation::constraints::{CollectionConstraint, ConstraintResult, ConstraintViolation};
2
3pub struct Count {
7 pub min: Option<usize>,
8 pub max: Option<usize>,
9 pub exact: Option<usize>,
10 pub min_message: String,
11 pub max_message: String,
12 pub exact_message: String,
13}
14
15impl Default for Count {
16 fn default() -> Self {
17 Self {
18 min: None,
19 max: None,
20 exact: None,
21 min_message: "This collection should contain {{ limit }} elements or more.".to_string(),
22 max_message: "This collection should contain {{ limit }} elements or less.".to_string(),
23 exact_message: "This collection should contain exactly {{ limit }} elements.".to_string(),
24 }
25 }
26}
27
28impl Count {
29 pub fn new() -> Self {
30 Self::default()
31 }
32
33 pub fn min(mut self, min: usize) -> Self {
34 self.min = Some(min);
35 self
36 }
37
38 pub fn max(mut self, max: usize) -> Self {
39 self.max = Some(max);
40 self
41 }
42
43 pub fn exact(mut self, exact: usize) -> Self {
44 self.exact = Some(exact);
45 self
46 }
47
48 pub fn between(min: usize, max: usize) -> Self {
49 Self {
50 min: Some(min),
51 max: Some(max),
52 ..Self::default()
53 }
54 }
55
56 fn format_message(template: &str, limit: usize) -> String {
57 template.replace("{{ limit }}", &limit.to_string())
58 }
59}
60
61impl CollectionConstraint for Count {
62 fn validate_slice<T>(&self, value: &[T]) -> ConstraintResult {
63 let len = value.len();
64
65 if let Some(exact) = self.exact {
66 if len != exact {
67 return Err(ConstraintViolation::new(
68 self.name(),
69 Self::format_message(&self.exact_message, exact),
70 format!("count: {}", len),
71 ));
72 }
73 return Ok(());
74 }
75
76 if let Some(min) = self.min {
77 if len < min {
78 return Err(ConstraintViolation::new(
79 self.name(),
80 Self::format_message(&self.min_message, min),
81 format!("count: {}", len),
82 ));
83 }
84 }
85
86 if let Some(max) = self.max {
87 if len > max {
88 return Err(ConstraintViolation::new(
89 self.name(),
90 Self::format_message(&self.max_message, max),
91 format!("count: {}", len),
92 ));
93 }
94 }
95
96 Ok(())
97 }
98
99 fn name(&self) -> &'static str {
100 "Count"
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn test_min_count() {
110 let constraint = Count::new().min(2);
111 assert!(constraint.validate_slice(&[1, 2]).is_ok());
112 assert!(constraint.validate_slice(&[1, 2, 3]).is_ok());
113 assert!(constraint.validate_slice(&[1]).is_err());
114 }
115
116 #[test]
117 fn test_max_count() {
118 let constraint = Count::new().max(3);
119 assert!(constraint.validate_slice(&[1, 2, 3]).is_ok());
120 assert!(constraint.validate_slice(&[1]).is_ok());
121 assert!(constraint.validate_slice(&[1, 2, 3, 4]).is_err());
122 }
123
124 #[test]
125 fn test_between_count() {
126 let constraint = Count::between(2, 4);
127 assert!(constraint.validate_slice(&[1, 2]).is_ok());
128 assert!(constraint.validate_slice(&[1, 2, 3, 4]).is_ok());
129 assert!(constraint.validate_slice(&[1]).is_err());
130 assert!(constraint.validate_slice(&[1, 2, 3, 4, 5]).is_err());
131 }
132
133 #[test]
134 fn test_exact_count() {
135 let constraint = Count::new().exact(3);
136 assert!(constraint.validate_slice(&[1, 2, 3]).is_ok());
137 assert!(constraint.validate_slice(&[1, 2]).is_err());
138 assert!(constraint.validate_slice(&[1, 2, 3, 4]).is_err());
139 }
140}