cratestack_core/
validators.rs1use crate::Decimal;
10use crate::error::CoolError;
11
12#[cfg(test)]
13mod tests;
14
15pub fn validate_length(
16 field: &'static str,
17 value: &str,
18 min: Option<usize>,
19 max: Option<usize>,
20) -> Result<(), CoolError> {
21 let len = value.chars().count();
22 if let Some(min) = min
23 && len < min
24 {
25 return Err(CoolError::Validation(format!(
26 "field '{field}' length {len} is below minimum {min}",
27 )));
28 }
29 if let Some(max) = max
30 && len > max
31 {
32 return Err(CoolError::Validation(format!(
33 "field '{field}' length {len} exceeds maximum {max}",
34 )));
35 }
36 Ok(())
37}
38
39pub fn validate_range_i64(
40 field: &'static str,
41 value: i64,
42 min: Option<i64>,
43 max: Option<i64>,
44) -> Result<(), CoolError> {
45 if let Some(min) = min
46 && value < min
47 {
48 return Err(CoolError::Validation(format!(
49 "field '{field}' is below minimum {min}",
50 )));
51 }
52 if let Some(max) = max
53 && value > max
54 {
55 return Err(CoolError::Validation(format!(
56 "field '{field}' exceeds maximum {max}",
57 )));
58 }
59 Ok(())
60}
61
62pub fn validate_range_decimal(
71 field: &'static str,
72 value: &Decimal,
73 min: Option<i64>,
74 max: Option<i64>,
75) -> Result<(), CoolError> {
76 if let Some(min) = min {
77 let bound = Decimal::from(min);
78 if *value < bound {
79 return Err(CoolError::Validation(format!(
80 "field '{field}' is below minimum {min}",
81 )));
82 }
83 }
84 if let Some(max) = max {
85 let bound = Decimal::from(max);
86 if *value > bound {
87 return Err(CoolError::Validation(format!(
88 "field '{field}' exceeds maximum {max}",
89 )));
90 }
91 }
92 Ok(())
93}
94
95pub fn validate_email(field: &'static str, value: &str) -> Result<(), CoolError> {
101 let trimmed = value.trim();
102 if trimmed.is_empty()
103 || trimmed.chars().any(char::is_whitespace)
104 || trimmed.chars().filter(|c| *c == '@').count() != 1
105 {
106 return Err(CoolError::Validation(format!(
107 "field '{field}' is not a valid email address",
108 )));
109 }
110 let (local, domain) = trimmed.split_once('@').unwrap();
111 if local.is_empty() || domain.is_empty() || !domain.contains('.') {
112 return Err(CoolError::Validation(format!(
113 "field '{field}' is not a valid email address",
114 )));
115 }
116 Ok(())
117}
118
119pub fn validate_uri(field: &'static str, value: &str) -> Result<(), CoolError> {
120 if url::Url::parse(value).is_err() {
121 return Err(CoolError::Validation(format!(
122 "field '{field}' is not a valid URI",
123 )));
124 }
125 Ok(())
126}
127
128pub fn validate_iso4217(field: &'static str, value: &str) -> Result<(), CoolError> {
133 if value.len() != 3 || !value.chars().all(|c| c.is_ascii_uppercase()) {
134 return Err(CoolError::Validation(format!(
135 "field '{field}' must be a 3-letter uppercase ISO 4217 code",
136 )));
137 }
138 Ok(())
139}