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}