Skip to main content

qubit_common/lang/argument/
string.rs

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