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    fn require_non_blank(&self, name: &str) -> ArgumentResult<&Self> {
242        if self.trim().is_empty() {
243            return Err(ArgumentError::new(format!(
244                "Parameter '{}' cannot be empty or contain only whitespace characters",
245                name
246            )));
247        }
248        Ok(self)
249    }
250
251    fn require_length_be(&self, name: &str, length: usize) -> ArgumentResult<&Self> {
252        let actual_length = self.len();
253        if actual_length != length {
254            return Err(ArgumentError::new(format!(
255                "Parameter '{}' length must be {} but was {}",
256                name, length, actual_length
257            )));
258        }
259        Ok(self)
260    }
261
262    fn require_length_at_least(&self, name: &str, min_length: usize) -> ArgumentResult<&Self> {
263        let actual_length = self.len();
264        if actual_length < min_length {
265            return Err(ArgumentError::new(format!(
266                "Parameter '{}' length must be at least {} but was {}",
267                name, min_length, actual_length
268            )));
269        }
270        Ok(self)
271    }
272
273    fn require_length_at_most(&self, name: &str, max_length: usize) -> ArgumentResult<&Self> {
274        let actual_length = self.len();
275        if actual_length > max_length {
276            return Err(ArgumentError::new(format!(
277                "Parameter '{}' length must be at most {} but was {}",
278                name, max_length, actual_length
279            )));
280        }
281        Ok(self)
282    }
283
284    fn require_length_in_range(
285        &self,
286        name: &str,
287        min_length: usize,
288        max_length: usize,
289    ) -> ArgumentResult<&Self> {
290        let actual_length = self.len();
291        if actual_length < min_length || actual_length > max_length {
292            return Err(ArgumentError::new(format!(
293                "Parameter '{}' length must be in range [{}, {}] but was {}",
294                name, min_length, max_length, actual_length
295            )));
296        }
297        Ok(self)
298    }
299
300    fn require_match(&self, name: &str, pattern: &Regex) -> ArgumentResult<&Self> {
301        if !pattern.is_match(self) {
302            return Err(ArgumentError::new(format!(
303                "Parameter '{}' must match pattern '{}'",
304                name,
305                pattern.as_str()
306            )));
307        }
308        Ok(self)
309    }
310
311    fn require_not_match(&self, name: &str, pattern: &Regex) -> ArgumentResult<&Self> {
312        if pattern.is_match(self) {
313            return Err(ArgumentError::new(format!(
314                "Parameter '{}' cannot match pattern '{}'",
315                name,
316                pattern.as_str()
317            )));
318        }
319        Ok(self)
320    }
321}
322
323impl StringArgument for String {
324    fn require_non_blank(&self, name: &str) -> ArgumentResult<&Self> {
325        if self.trim().is_empty() {
326            return Err(ArgumentError::new(format!(
327                "Parameter '{}' cannot be empty or contain only whitespace characters",
328                name
329            )));
330        }
331        Ok(self)
332    }
333
334    fn require_length_be(&self, name: &str, length: usize) -> ArgumentResult<&Self> {
335        let actual_length = self.len();
336        if actual_length != length {
337            return Err(ArgumentError::new(format!(
338                "Parameter '{}' length must be {} but was {}",
339                name, length, actual_length
340            )));
341        }
342        Ok(self)
343    }
344
345    fn require_length_at_least(&self, name: &str, min_length: usize) -> ArgumentResult<&Self> {
346        let actual_length = self.len();
347        if actual_length < min_length {
348            return Err(ArgumentError::new(format!(
349                "Parameter '{}' length must be at least {} but was {}",
350                name, min_length, actual_length
351            )));
352        }
353        Ok(self)
354    }
355
356    fn require_length_at_most(&self, name: &str, max_length: usize) -> ArgumentResult<&Self> {
357        let actual_length = self.len();
358        if actual_length > max_length {
359            return Err(ArgumentError::new(format!(
360                "Parameter '{}' length must be at most {} but was {}",
361                name, max_length, actual_length
362            )));
363        }
364        Ok(self)
365    }
366
367    fn require_length_in_range(
368        &self,
369        name: &str,
370        min_length: usize,
371        max_length: usize,
372    ) -> ArgumentResult<&Self> {
373        let actual_length = self.len();
374        if actual_length < min_length || actual_length > max_length {
375            return Err(ArgumentError::new(format!(
376                "Parameter '{}' length must be in range [{}, {}] but was {}",
377                name, min_length, max_length, actual_length
378            )));
379        }
380        Ok(self)
381    }
382
383    fn require_match(&self, name: &str, pattern: &Regex) -> ArgumentResult<&Self> {
384        if !pattern.is_match(self) {
385            return Err(ArgumentError::new(format!(
386                "Parameter '{}' must match pattern '{}'",
387                name,
388                pattern.as_str()
389            )));
390        }
391        Ok(self)
392    }
393
394    fn require_not_match(&self, name: &str, pattern: &Regex) -> ArgumentResult<&Self> {
395        if pattern.is_match(self) {
396            return Err(ArgumentError::new(format!(
397                "Parameter '{}' cannot match pattern '{}'",
398                name,
399                pattern.as_str()
400            )));
401        }
402        Ok(self)
403    }
404}