Skip to main content

wasm_dbms_api/dbms/validate/
case.rs

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