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}