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}