ic_dbms_api/validate/
case.rs

1use ic_dbms_api::prelude::Value;
2
3use crate::prelude::Validate;
4
5/// A validator for `snake_case` strings.
6///
7/// Rules:
8/// - Only lowercase letters, digits, and underscores are allowed.
9/// - The string must start with a lowercase letter or an underscore.
10///
11/// # Examples of valid snake_case
12///
13/// - `valid_snake_case`
14/// - `_leading_underscore`
15/// - `snake_case_123`
16///
17/// # Examples of invalid snake_case
18///
19/// - `Invalid_Snake_Case` (contains uppercase letters)
20/// - `invalid-snake-case!` (contains special characters)
21/// - `1invalid_snake_case` (starts with a digit)
22///
23/// # Example
24///
25/// ```rust
26/// use ic_dbms_api::prelude::{SnakeCaseValidator, Value, Validate};
27///
28/// let validator = SnakeCaseValidator;
29/// let value = Value::Text(ic_dbms_api::prelude::Text("valid_snake_case".into()));
30/// assert!(validator.validate(&value).is_ok());
31/// ```
32pub struct SnakeCaseValidator;
33
34impl Validate for SnakeCaseValidator {
35    fn validate(&self, value: &Value) -> ic_dbms_api::prelude::IcDbmsResult<()> {
36        let Value::Text(text) = value else {
37            return Err(ic_dbms_api::prelude::IcDbmsError::Validation(
38                "RGB color validation requires a text value".to_string(),
39            ));
40        };
41
42        let s = &text.0;
43
44        // first must be lowercase letter or underscore
45        let first_char = s.chars().next().ok_or_else(|| {
46            crate::prelude::IcDbmsError::Validation(
47                "Empty string is not valid snake_case".to_string(),
48            )
49        })?;
50        if !first_char.is_lowercase() && first_char != '_' {
51            return Err(crate::prelude::IcDbmsError::Validation(format!(
52                "Value '{s}' is not in snake_case format",
53            )));
54        }
55
56        if s.chars()
57            .all(|c| c.is_lowercase() || c.is_ascii_digit() || c == '_')
58        {
59            Ok(())
60        } else {
61            Err(crate::prelude::IcDbmsError::Validation(format!(
62                "Value '{s}' is not in snake_case format",
63            )))
64        }
65    }
66}
67
68/// A validator for `kebab-case` strings.
69///
70/// Rules:
71/// - Only lowercase letters, digits, and hyphens are allowed.
72/// - The string must start with a lowercase letter.
73///
74/// # Examples of valid kebab-case
75///
76/// - `valid-kebab-case`
77/// - `kebab-case-123`
78///
79/// # Examples of invalid kebab-case
80///
81/// - `Invalid-Kebab-Case` (contains uppercase letters)
82/// - `invalid_kebab_case!` (contains special characters)
83/// - `1invalid-kebab-case` (starts with a digit)
84///
85/// # Example
86///
87/// ```rust
88/// use ic_dbms_api::prelude::{KebabCaseValidator, Value, Validate};
89/// let validator = KebabCaseValidator;
90/// let value = Value::Text(ic_dbms_api::prelude::Text("valid-kebab-case".into()));
91/// assert!(validator.validate(&value).is_ok());
92/// ```
93pub struct KebabCaseValidator;
94
95impl Validate for KebabCaseValidator {
96    fn validate(&self, value: &Value) -> ic_dbms_api::prelude::IcDbmsResult<()> {
97        let Value::Text(text) = value else {
98            return Err(ic_dbms_api::prelude::IcDbmsError::Validation(
99                "RGB color validation requires a text value".to_string(),
100            ));
101        };
102
103        let s = &text.0;
104
105        // first must be lowercase letter
106        let first_char = s.chars().next().ok_or_else(|| {
107            crate::prelude::IcDbmsError::Validation(
108                "Empty string is not valid kebab-case".to_string(),
109            )
110        })?;
111        if !first_char.is_lowercase() {
112            return Err(crate::prelude::IcDbmsError::Validation(format!(
113                "Value '{s}' is not in kebab-case format",
114            )));
115        }
116
117        if s.chars()
118            .all(|c| c.is_lowercase() || c.is_ascii_digit() || c == '-')
119        {
120            Ok(())
121        } else {
122            Err(crate::prelude::IcDbmsError::Validation(format!(
123                "Value '{s}' is not in kebab-case format",
124            )))
125        }
126    }
127}
128
129/// A validator for `CamelCase` strings.
130///
131/// Rules:
132/// - The string must start with an uppercase letter.
133/// - Only alphanumeric characters are allowed (no spaces, underscores, or special characters).
134///
135/// # Examples of valid CamelCase
136/// - `ValidCamelCase`
137/// - `AnotherExample123`
138///
139/// # Examples of invalid CamelCase
140///
141/// - `invalidCamelCase` (starts with a lowercase letter
142/// - `Invalid-CamelCase!` (contains special characters)
143/// - `Invalid_CamelCase` (contains underscores)
144///
145/// # Example
146///
147/// ```rust
148/// use ic_dbms_api::prelude::{CamelCaseValidator, Value, Validate};
149/// let validator = CamelCaseValidator;
150/// let value = Value::Text(ic_dbms_api::prelude::Text("ValidCamelCase".into()));
151/// assert!(validator.validate(&value).is_ok());
152/// ```
153pub struct CamelCaseValidator;
154
155impl Validate for CamelCaseValidator {
156    fn validate(&self, value: &Value) -> ic_dbms_api::prelude::IcDbmsResult<()> {
157        let Value::Text(text) = value else {
158            return Err(ic_dbms_api::prelude::IcDbmsError::Validation(
159                "CamelCase validation requires a text value".to_string(),
160            ));
161        };
162
163        let s = &text.0;
164
165        let mut chars = s.chars();
166        let first_char = chars.next().ok_or_else(|| {
167            crate::prelude::IcDbmsError::Validation(
168                "Empty string is not valid CamelCase".to_string(),
169            )
170        })?;
171        if !first_char.is_uppercase() {
172            return Err(crate::prelude::IcDbmsError::Validation(format!(
173                "Value '{s}' is not in CamelCase format"
174            )));
175        }
176
177        if s.chars().all(|c| c.is_alphanumeric()) {
178            Ok(())
179        } else {
180            Err(crate::prelude::IcDbmsError::Validation(format!(
181                "Value '{}' is not in CamelCase format",
182                s
183            )))
184        }
185    }
186}
187
188#[cfg(test)]
189mod tests {
190    use ic_dbms_api::prelude::Value;
191
192    use super::*;
193
194    #[test]
195    fn test_snake_case_validator() {
196        let validator = SnakeCaseValidator;
197
198        // Valid snake_case
199        let value = Value::Text(ic_dbms_api::prelude::Text("valid_snake_case".to_string()));
200        assert!(validator.validate(&value).is_ok());
201
202        // Invalid snake_case (uppercase letter)
203        let value = Value::Text(ic_dbms_api::prelude::Text("Invalid_Snake_Case".to_string()));
204        assert!(validator.validate(&value).is_err());
205
206        // Invalid snake_case (special character)
207        let value = Value::Text(ic_dbms_api::prelude::Text(
208            "invalid-snake-case!".to_string(),
209        ));
210        assert!(validator.validate(&value).is_err());
211
212        // Invalid snake_case (starts with digit)
213        let value = Value::Text(ic_dbms_api::prelude::Text(
214            "1invalid_snake_case".to_string(),
215        ));
216        assert!(validator.validate(&value).is_err());
217
218        // Valid snake_case (starts with underscore)
219        let value = Value::Text(ic_dbms_api::prelude::Text("_valid_snake_case".to_string()));
220        assert!(validator.validate(&value).is_ok());
221
222        // empty string
223        let value = Value::Text(ic_dbms_api::prelude::Text("".to_string()));
224        assert!(validator.validate(&value).is_err());
225
226        let value = Value::Int32(123i32.into());
227        assert!(validator.validate(&value).is_err());
228    }
229
230    #[test]
231    fn test_kebab_case_validator() {
232        let validator = KebabCaseValidator;
233
234        // Valid kebab-case
235        let value = Value::Text(ic_dbms_api::prelude::Text("valid-kebab-case".to_string()));
236        assert!(validator.validate(&value).is_ok());
237
238        // Invalid kebab-case (uppercase letter)
239        let value = Value::Text(ic_dbms_api::prelude::Text("Invalid-Kebab-Case".to_string()));
240        assert!(validator.validate(&value).is_err());
241
242        // Invalid kebab-case (special character)
243        let value = Value::Text(ic_dbms_api::prelude::Text(
244            "invalid-kebab-case!".to_string(),
245        ));
246        assert!(validator.validate(&value).is_err());
247
248        // empty string
249        let value = Value::Text(ic_dbms_api::prelude::Text("".to_string()));
250        assert!(validator.validate(&value).is_err());
251
252        // Invalid kebab-case (starts with digit)
253        let value = Value::Text(ic_dbms_api::prelude::Text(
254            "1invalid-kebab-case".to_string(),
255        ));
256        assert!(validator.validate(&value).is_err());
257
258        let value = Value::Int32(123i32.into());
259        assert!(validator.validate(&value).is_err());
260    }
261
262    #[test]
263    fn test_camel_case_validator() {
264        let validator = CamelCaseValidator;
265        // Valid CamelCase
266        let value = Value::Text(ic_dbms_api::prelude::Text("ValidCamelCase".to_string()));
267        assert!(validator.validate(&value).is_ok());
268        // Invalid CamelCase (starts with lowercase)
269        let value = Value::Text(ic_dbms_api::prelude::Text("invalidCamelCase".to_string()));
270        assert!(validator.validate(&value).is_err());
271        // Invalid CamelCase (special character)
272        let value = Value::Text(ic_dbms_api::prelude::Text("Invalid-CamelCase!".to_string()));
273        assert!(validator.validate(&value).is_err());
274        // Invalid CamelCase (contains underscore)
275        let value = Value::Text(ic_dbms_api::prelude::Text("Invalid_CamelCase".to_string()));
276        assert!(validator.validate(&value).is_err());
277
278        // empty string
279        let value = Value::Text(ic_dbms_api::prelude::Text("".to_string()));
280        assert!(validator.validate(&value).is_err());
281
282        let value = Value::Int32(123i32.into());
283        assert!(validator.validate(&value).is_err());
284    }
285}