prism3_core/lang/argument/
string.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025.
4 *    3-Prism Co. Ltd.
5 *
6 *    All rights reserved.
7 *
8 ******************************************************************************/
9//! # String Argument Validation
10//!
11//! Provides validation functionality for string type arguments.
12//!
13//! # Author
14//!
15//! Haixing Hu
16
17use super::error::{ArgumentError, ArgumentResult};
18use regex::Regex;
19
20/// String argument validation trait
21///
22/// Provides length, content, and format validation functionality for string types.
23///
24/// # Features
25///
26/// - Length validation support
27/// - Blank checking support
28/// - Regular expression matching support
29/// - Method chaining support
30///
31/// # Use Cases
32///
33/// - User input validation
34/// - Configuration parameter checking
35/// - Text content validation
36///
37/// # Author
38///
39/// Haixing Hu
40///
41/// # Examples
42///
43/// Basic usage (returns `ArgumentResult`):
44///
45/// ```rust,ignore
46/// use prism3_core::lang::argument::{StringArgument, ArgumentResult};
47///
48/// fn set_username(username: &str) -> ArgumentResult<()> {
49///     let username = username
50///         .require_non_blank("username")?
51///         .require_length_in_range("username", 3, 20)?;
52///     println!("Username: {}", username);
53///     Ok(())
54/// }
55/// ```
56///
57/// Converting to other error types:
58///
59/// ```rust,ignore
60/// use prism3_core::lang::argument::StringArgument;
61///
62/// fn set_username(username: &str) -> Result<(), String> {
63///     let username = username
64///         .require_non_blank("username")
65///         .and_then(|u| u.require_length_in_range("username", 3, 20))
66///         .map_err(|e| e.to_string())?;
67///     println!("Username: {}", username);
68///     Ok(())
69/// }
70/// ```
71pub trait StringArgument {
72    /// Validate that string is not blank
73    ///
74    /// Checks if the string is empty or contains only whitespace characters.
75    ///
76    /// # Parameters
77    ///
78    /// * `name` - Parameter name
79    ///
80    /// # Returns
81    ///
82    /// Returns `Ok(self)` if string is not blank, otherwise returns an error
83    ///
84    /// # Examples
85    ///
86    /// ```rust,ignore
87    /// use prism3_core::lang::argument::StringArgument;
88    ///
89    /// let text = "hello";
90    /// assert!(text.require_non_blank("text").is_ok());
91    ///
92    /// let blank = "   ";
93    /// assert!(blank.require_non_blank("text").is_err());
94    /// ```
95    fn require_non_blank(&self, name: &str) -> ArgumentResult<&Self>;
96
97    /// Validate that string length equals the specified value
98    ///
99    /// # Parameters
100    ///
101    /// * `name` - Parameter name
102    /// * `length` - Expected length
103    ///
104    /// # Returns
105    ///
106    /// Returns `Ok(self)` if length matches, otherwise returns an error
107    ///
108    /// # Examples
109    ///
110    /// ```rust,ignore
111    /// use prism3_core::lang::argument::StringArgument;
112    ///
113    /// let code = "ABC12";
114    /// assert!(code.require_length_be("code", 5).is_ok());
115    ///
116    /// let wrong_length = "ABC";
117    /// assert!(wrong_length.require_length_be("code", 5).is_err());
118    /// ```
119    fn require_length_be(&self, name: &str, length: usize) -> ArgumentResult<&Self>;
120
121    /// Validate that string length is at least the specified value
122    ///
123    /// # Parameters
124    ///
125    /// * `name` - Parameter name
126    /// * `min_length` - Minimum length
127    ///
128    /// # Returns
129    ///
130    /// Returns `Ok(self)` if length is not less than minimum, otherwise returns an error
131    ///
132    /// # Examples
133    ///
134    /// ```rust,ignore
135    /// use prism3_core::lang::argument::StringArgument;
136    ///
137    /// let password = "secret123";
138    /// assert!(password.require_length_at_least("password", 8).is_ok());
139    /// ```
140    fn require_length_at_least(&self, name: &str, min_length: usize) -> ArgumentResult<&Self>;
141
142    /// Validate that string length is at most the specified value
143    ///
144    /// # Parameters
145    ///
146    /// * `name` - Parameter name
147    /// * `max_length` - Maximum length
148    ///
149    /// # Returns
150    ///
151    /// Returns `Ok(self)` if length is not greater than maximum, otherwise returns an error
152    ///
153    /// # Examples
154    ///
155    /// ```rust,ignore
156    /// use prism3_core::lang::argument::StringArgument;
157    ///
158    /// let description = "Short text";
159    /// assert!(description.require_length_at_most("description", 100).is_ok());
160    /// ```
161    fn require_length_at_most(&self, name: &str, max_length: usize) -> ArgumentResult<&Self>;
162
163    /// Validate that string length is within the specified range
164    ///
165    /// # Parameters
166    ///
167    /// * `name` - Parameter name
168    /// * `min_length` - Minimum length (inclusive)
169    /// * `max_length` - Maximum length (inclusive)
170    ///
171    /// # Returns
172    ///
173    /// Returns `Ok(self)` if length is within range, otherwise returns an error
174    ///
175    /// # Examples
176    ///
177    /// ```rust,ignore
178    /// use prism3_core::lang::argument::StringArgument;
179    ///
180    /// let username = "alice";
181    /// assert!(username.require_length_in_range("username", 3, 20).is_ok());
182    /// ```
183    fn require_length_in_range(
184        &self,
185        name: &str,
186        min_length: usize,
187        max_length: usize,
188    ) -> ArgumentResult<&Self>;
189
190    /// Validate that string matches regular expression
191    ///
192    /// # Parameters
193    ///
194    /// * `name` - Parameter name
195    /// * `pattern` - Regular expression
196    ///
197    /// # Returns
198    ///
199    /// Returns `Ok(self)` if matches, otherwise returns an error
200    ///
201    /// # Examples
202    ///
203    /// ```rust,ignore
204    /// use prism3_core::lang::argument::StringArgument;
205    /// use regex::Regex;
206    ///
207    /// let email = "user@example.com";
208    /// let pattern = Regex::new(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$").unwrap();
209    /// assert!(email.require_match("email", &pattern).is_ok());
210    /// ```
211    fn require_match(&self, name: &str, pattern: &Regex) -> ArgumentResult<&Self>;
212
213    /// Validate that string does not match regular expression
214    ///
215    /// # Parameters
216    ///
217    /// * `name` - Parameter name
218    /// * `pattern` - Regular expression
219    ///
220    /// # Returns
221    ///
222    /// Returns `Ok(self)` if does not match, otherwise returns an error
223    ///
224    /// # Examples
225    ///
226    /// ```rust,ignore
227    /// use prism3_core::lang::argument::StringArgument;
228    /// use regex::Regex;
229    ///
230    /// let text = "hello world";
231    /// let pattern = Regex::new(r"\d+").unwrap();
232    /// assert!(text.require_not_match("text", &pattern).is_ok());
233    /// ```
234    fn require_not_match(&self, name: &str, pattern: &Regex) -> ArgumentResult<&Self>;
235}
236
237impl StringArgument for str {
238    fn require_non_blank(&self, name: &str) -> ArgumentResult<&Self> {
239        if self.trim().is_empty() {
240            return Err(ArgumentError::new(format!(
241                "Parameter '{}' cannot be empty or contain only whitespace characters",
242                name
243            )));
244        }
245        Ok(self)
246    }
247
248    fn require_length_be(&self, name: &str, length: usize) -> ArgumentResult<&Self> {
249        let actual_length = self.len();
250        if actual_length != length {
251            return Err(ArgumentError::new(format!(
252                "Parameter '{}' length must be {} but was {}",
253                name, length, actual_length
254            )));
255        }
256        Ok(self)
257    }
258
259    fn require_length_at_least(&self, name: &str, min_length: usize) -> ArgumentResult<&Self> {
260        let actual_length = self.len();
261        if actual_length < min_length {
262            return Err(ArgumentError::new(format!(
263                "Parameter '{}' length must be at least {} but was {}",
264                name, min_length, actual_length
265            )));
266        }
267        Ok(self)
268    }
269
270    fn require_length_at_most(&self, name: &str, max_length: usize) -> ArgumentResult<&Self> {
271        let actual_length = self.len();
272        if actual_length > max_length {
273            return Err(ArgumentError::new(format!(
274                "Parameter '{}' length must be at most {} but was {}",
275                name, max_length, actual_length
276            )));
277        }
278        Ok(self)
279    }
280
281    fn require_length_in_range(
282        &self,
283        name: &str,
284        min_length: usize,
285        max_length: usize,
286    ) -> ArgumentResult<&Self> {
287        let actual_length = self.len();
288        if actual_length < min_length || actual_length > max_length {
289            return Err(ArgumentError::new(format!(
290                "Parameter '{}' length must be in range [{}, {}] but was {}",
291                name, min_length, max_length, actual_length
292            )));
293        }
294        Ok(self)
295    }
296
297    fn require_match(&self, name: &str, pattern: &Regex) -> ArgumentResult<&Self> {
298        if !pattern.is_match(self) {
299            return Err(ArgumentError::new(format!(
300                "Parameter '{}' must match pattern '{}'",
301                name,
302                pattern.as_str()
303            )));
304        }
305        Ok(self)
306    }
307
308    fn require_not_match(&self, name: &str, pattern: &Regex) -> ArgumentResult<&Self> {
309        if pattern.is_match(self) {
310            return Err(ArgumentError::new(format!(
311                "Parameter '{}' cannot match pattern '{}'",
312                name,
313                pattern.as_str()
314            )));
315        }
316        Ok(self)
317    }
318}
319
320impl StringArgument for String {
321    fn require_non_blank(&self, name: &str) -> ArgumentResult<&Self> {
322        if self.trim().is_empty() {
323            return Err(ArgumentError::new(format!(
324                "Parameter '{}' cannot be empty or contain only whitespace characters",
325                name
326            )));
327        }
328        Ok(self)
329    }
330
331    fn require_length_be(&self, name: &str, length: usize) -> ArgumentResult<&Self> {
332        let actual_length = self.len();
333        if actual_length != length {
334            return Err(ArgumentError::new(format!(
335                "Parameter '{}' length must be {} but was {}",
336                name, length, actual_length
337            )));
338        }
339        Ok(self)
340    }
341
342    fn require_length_at_least(&self, name: &str, min_length: usize) -> ArgumentResult<&Self> {
343        let actual_length = self.len();
344        if actual_length < min_length {
345            return Err(ArgumentError::new(format!(
346                "Parameter '{}' length must be at least {} but was {}",
347                name, min_length, actual_length
348            )));
349        }
350        Ok(self)
351    }
352
353    fn require_length_at_most(&self, name: &str, max_length: usize) -> ArgumentResult<&Self> {
354        let actual_length = self.len();
355        if actual_length > max_length {
356            return Err(ArgumentError::new(format!(
357                "Parameter '{}' length must be at most {} but was {}",
358                name, max_length, actual_length
359            )));
360        }
361        Ok(self)
362    }
363
364    fn require_length_in_range(
365        &self,
366        name: &str,
367        min_length: usize,
368        max_length: usize,
369    ) -> ArgumentResult<&Self> {
370        let actual_length = self.len();
371        if actual_length < min_length || actual_length > max_length {
372            return Err(ArgumentError::new(format!(
373                "Parameter '{}' length must be in range [{}, {}] but was {}",
374                name, min_length, max_length, actual_length
375            )));
376        }
377        Ok(self)
378    }
379
380    fn require_match(&self, name: &str, pattern: &Regex) -> ArgumentResult<&Self> {
381        if !pattern.is_match(self) {
382            return Err(ArgumentError::new(format!(
383                "Parameter '{}' must match pattern '{}'",
384                name,
385                pattern.as_str()
386            )));
387        }
388        Ok(self)
389    }
390
391    fn require_not_match(&self, name: &str, pattern: &Regex) -> ArgumentResult<&Self> {
392        if pattern.is_match(self) {
393            return Err(ArgumentError::new(format!(
394                "Parameter '{}' cannot match pattern '{}'",
395                name,
396                pattern.as_str()
397            )));
398        }
399        Ok(self)
400    }
401}