1use crate::error::{ValidationErrors, ValidationResult};
4use crate::traits::{ValidateField, ValidateRequest, ValidationRule};
5use crate::validators::*;
6use async_trait::async_trait;
7use serde_json::Value;
8use std::collections::HashMap;
9use std::sync::Arc;
10use service_builder::builder;
11
12#[derive(Clone)]
14pub struct Rules {
15 field_rules: HashMap<String, Vec<Arc<dyn ValidationRule>>>,
17 request_rules: Vec<Arc<dyn ValidationRule>>,
19}
20
21impl std::fmt::Debug for Rules {
22 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23 f.debug_struct("Rules")
24 .field("field_rules_count", &self.field_rules.len())
25 .field("request_rules_count", &self.request_rules.len())
26 .field("validated_fields", &self.get_validated_fields())
27 .finish()
28 }
29}
30
31impl Rules {
32 pub fn new() -> Self {
34 Self {
35 field_rules: HashMap::new(),
36 request_rules: Vec::new(),
37 }
38 }
39
40 pub fn field<R>(mut self, field: impl Into<String>, rule: R) -> Self
42 where
43 R: ValidationRule + 'static,
44 {
45 let field = field.into();
46 self.field_rules
47 .entry(field)
48 .or_default()
49 .push(Arc::new(rule));
50 self
51 }
52
53 pub fn field_rules<R>(mut self, field: impl Into<String>, rules: Vec<R>) -> Self
55 where
56 R: ValidationRule + 'static,
57 {
58 let field = field.into();
59 let rule_arcs: Vec<Arc<dyn ValidationRule>> = rules
60 .into_iter()
61 .map(|r| Arc::new(r) as Arc<dyn ValidationRule>)
62 .collect();
63
64 self.field_rules
65 .entry(field)
66 .or_default()
67 .extend(rule_arcs);
68 self
69 }
70
71 pub fn request<R>(mut self, rule: R) -> Self
73 where
74 R: ValidationRule + 'static,
75 {
76 self.request_rules.push(Arc::new(rule));
77 self
78 }
79
80 pub fn get_field_rules(&self, field: &str) -> Option<&Vec<Arc<dyn ValidationRule>>> {
82 self.field_rules.get(field)
83 }
84
85 pub fn get_request_rules(&self) -> &Vec<Arc<dyn ValidationRule>> {
87 &self.request_rules
88 }
89
90 pub fn is_empty(&self) -> bool {
92 self.field_rules.is_empty() && self.request_rules.is_empty()
93 }
94
95 pub fn field_rule_count(&self) -> usize {
97 self.field_rules.len()
98 }
99
100 pub fn request_rule_count(&self) -> usize {
102 self.request_rules.len()
103 }
104
105 pub fn get_validated_fields(&self) -> Vec<&String> {
107 self.field_rules.keys().collect()
108 }
109}
110
111impl Default for Rules {
112 fn default() -> Self {
113 Self::new()
114 }
115}
116
117#[async_trait]
118impl ValidateField for Rules {
119 async fn validate_field(&self, field: &str, value: &Value) -> ValidationResult<()> {
120 if let Some(rules) = self.field_rules.get(field) {
121 let mut errors = ValidationErrors::new();
122
123 for rule in rules {
124 if let Err(rule_errors) = rule.validate(value, field).await {
125 errors.merge(rule_errors);
126 }
127 }
128
129 if errors.is_empty() {
130 Ok(())
131 } else {
132 Err(errors)
133 }
134 } else {
135 Ok(())
137 }
138 }
139}
140
141#[async_trait]
142impl ValidateRequest for Rules {
143 async fn validate_request(&self, data: &HashMap<String, Value>) -> ValidationResult<()> {
144 let mut errors = ValidationErrors::new();
145
146 for rule in &self.request_rules {
148 let data_value = Value::Object(
150 data.iter()
151 .map(|(k, v)| (k.clone(), v.clone()))
152 .collect::<serde_json::Map<String, Value>>()
153 );
154
155 if let Err(rule_errors) = rule.validate(&data_value, "request").await {
156 errors.merge(rule_errors);
157 }
158 }
159
160 if errors.is_empty() {
161 Ok(())
162 } else {
163 Err(errors)
164 }
165 }
166}
167
168#[derive(Clone)]
170#[builder]
171pub struct RulesBuilderConfig {
172 #[builder(default)]
173 pub field_rules: HashMap<String, Vec<Arc<dyn ValidationRule>>>,
174
175 #[builder(default)]
176 pub request_rules: Vec<Arc<dyn ValidationRule>>,
177}
178
179impl std::fmt::Debug for RulesBuilderConfig {
180 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
181 f.debug_struct("RulesBuilderConfig")
182 .field("field_rules_count", &self.field_rules.len())
183 .field("request_rules_count", &self.request_rules.len())
184 .finish()
185 }
186}
187
188impl RulesBuilderConfig {
189 pub fn build_rules(self) -> Rules {
191 Rules {
192 field_rules: self.field_rules,
193 request_rules: self.request_rules,
194 }
195 }
196}
197
198impl RulesBuilderConfigBuilder {
200 pub fn field_rule<R>(self, field: impl Into<String>, rule: R) -> Self
202 where
203 R: ValidationRule + 'static,
204 {
205 let field = field.into();
206 let mut field_rules = self.field_rules.unwrap_or_default();
207 field_rules
208 .entry(field)
209 .or_default()
210 .push(Arc::new(rule));
211 RulesBuilderConfigBuilder {
212 field_rules: Some(field_rules),
213 request_rules: self.request_rules,
214 }
215 }
216
217 pub fn field_rules_vec<R>(self, field: impl Into<String>, rules: Vec<R>) -> Self
219 where
220 R: ValidationRule + 'static,
221 {
222 let field = field.into();
223 let rule_arcs: Vec<Arc<dyn ValidationRule>> = rules
224 .into_iter()
225 .map(|r| Arc::new(r) as Arc<dyn ValidationRule>)
226 .collect();
227
228 let mut field_rules = self.field_rules.unwrap_or_default();
229 field_rules
230 .entry(field)
231 .or_default()
232 .extend(rule_arcs);
233 RulesBuilderConfigBuilder {
234 field_rules: Some(field_rules),
235 request_rules: self.request_rules,
236 }
237 }
238
239 pub fn request_rule<R>(self, rule: R) -> Self
241 where
242 R: ValidationRule + 'static,
243 {
244 let mut request_rules = self.request_rules.unwrap_or_default();
245 request_rules.push(Arc::new(rule));
246 RulesBuilderConfigBuilder {
247 field_rules: self.field_rules,
248 request_rules: Some(request_rules),
249 }
250 }
251
252 pub fn build_config(self) -> RulesBuilderConfig {
253 self.build_with_defaults().unwrap()
254 }
255}
256
257pub struct RulesBuilder {
259 builder_config: RulesBuilderConfigBuilder,
260}
261
262impl RulesBuilder {
263 pub fn new() -> Self {
265 Self {
266 builder_config: RulesBuilderConfig::builder(),
267 }
268 }
269
270 pub fn build(self) -> Rules {
272 self.builder_config.build_config().build_rules()
273 }
274
275 pub fn required_string(
277 mut self,
278 field: impl Into<String>,
279 min_length: Option<usize>,
280 max_length: Option<usize>
281 ) -> Self {
282 let field = field.into();
283
284 self.builder_config = self.builder_config.field_rule(field.clone(), RequiredValidator::new());
286
287 if min_length.is_some() || max_length.is_some() {
289 let mut length_validator = LengthValidator::new();
290 if let Some(min) = min_length {
291 length_validator = length_validator.min(min);
292 }
293 if let Some(max) = max_length {
294 length_validator = length_validator.max(max);
295 }
296 self.builder_config = self.builder_config.field_rule(field, length_validator);
297 }
298
299 self
300 }
301
302 pub fn required_email(mut self, field: impl Into<String>) -> Self {
304 let field = field.into();
305
306 self.builder_config = self.builder_config
307 .field_rule(field.clone(), RequiredValidator::new())
308 .field_rule(field, EmailValidator::new());
309
310 self
311 }
312
313 pub fn optional_email(mut self, field: impl Into<String>) -> Self {
315 let field = field.into();
316
317 self.builder_config = self.builder_config.field_rule(field, EmailValidator::new());
319
320 self
321 }
322
323 pub fn required_number(
325 mut self,
326 field: impl Into<String>,
327 min: Option<f64>,
328 max: Option<f64>
329 ) -> Self {
330 let field = field.into();
331
332 self.builder_config = self.builder_config.field_rule(field.clone(), RequiredValidator::new());
333
334 let mut numeric_validator = NumericValidator::new();
335 if let Some(min_val) = min {
336 numeric_validator = numeric_validator.min(min_val);
337 }
338 if let Some(max_val) = max {
339 numeric_validator = numeric_validator.max(max_val);
340 }
341
342 self.builder_config = self.builder_config.field_rule(field, numeric_validator);
343
344 self
345 }
346
347 pub fn required_integer(
349 mut self,
350 field: impl Into<String>,
351 min: Option<f64>,
352 max: Option<f64>
353 ) -> Self {
354 let field = field.into();
355
356 self.builder_config = self.builder_config.field_rule(field.clone(), RequiredValidator::new());
357
358 let mut numeric_validator = NumericValidator::new().integer_only(true);
359 if let Some(min_val) = min {
360 numeric_validator = numeric_validator.min(min_val);
361 }
362 if let Some(max_val) = max {
363 numeric_validator = numeric_validator.max(max_val);
364 }
365
366 self.builder_config = self.builder_config.field_rule(field, numeric_validator);
367
368 self
369 }
370
371 pub fn pattern(mut self, field: impl Into<String>, pattern: &str) -> Self {
373 let field = field.into();
374
375 if let Ok(pattern_validator) = PatternValidator::new(pattern) {
376 self.builder_config = self.builder_config.field_rule(field, pattern_validator);
377 }
378
379 self
380 }
381
382 pub fn one_of(mut self, field: impl Into<String>, allowed_values: Vec<String>) -> Self {
384 let field = field.into();
385
386 let custom_validator = CustomValidator::one_of(
387 format!("{}_one_of", field),
388 allowed_values
389 );
390
391 self.builder_config = self.builder_config.field_rule(field, custom_validator);
392
393 self
394 }
395
396 pub fn custom<R>(mut self, field: impl Into<String>, rule: R) -> Self
398 where
399 R: ValidationRule + 'static,
400 {
401 self.builder_config = self.builder_config.field_rule(field, rule);
402 self
403 }
404
405 pub fn request_rule<R>(mut self, rule: R) -> Self
407 where
408 R: ValidationRule + 'static,
409 {
410 self.builder_config = self.builder_config.request_rule(rule);
411 self
412 }
413}
414
415impl Default for RulesBuilder {
416 fn default() -> Self {
417 Self::new()
418 }
419}
420
421#[cfg(test)]
422mod tests {
423 use super::*;
424 use crate::error::ValidationError;
425 use crate::traits::Validate;
426
427 #[tokio::test]
428 async fn test_rules_field_validation() {
429 let rules = Rules::new()
430 .field("name", RequiredValidator::new())
431 .field("name", LengthValidator::new().min(2).max(50))
432 .field("email", EmailValidator::new());
433
434 let result = rules.validate_field("name", &Value::String("John".to_string())).await;
436 assert!(result.is_ok());
437
438 let result = rules.validate_field("name", &Value::String("J".to_string())).await;
440 assert!(result.is_err());
441
442 let result = rules.validate_field("email", &Value::String("john@example.com".to_string())).await;
444 assert!(result.is_ok());
445
446 let result = rules.validate_field("email", &Value::String("not-an-email".to_string())).await;
448 assert!(result.is_err());
449 }
450
451 #[tokio::test]
452 async fn test_rules_request_validation() {
453 let password_confirmation_rule = CustomValidator::new("password_confirmation", |value, _field| {
454 if let Some(obj) = value.as_object() {
455 let password = obj.get("password").and_then(|v| v.as_str());
456 let confirmation = obj.get("password_confirmation").and_then(|v| v.as_str());
457
458 match (password, confirmation) {
459 (Some(pwd), Some(conf)) if pwd == conf => Ok(()),
460 (Some(_), Some(_)) => Err(ValidationError::new("password_confirmation", "Passwords do not match").into()),
461 (Some(_), None) => Err(ValidationError::new("password_confirmation", "Password confirmation is required").into()),
462 _ => Ok(()), }
464 } else {
465 Ok(())
466 }
467 });
468
469 let rules = Rules::new().request(password_confirmation_rule);
470
471 let mut data = HashMap::new();
473 data.insert("password".to_string(), Value::String("secret123".to_string()));
474 data.insert("password_confirmation".to_string(), Value::String("secret123".to_string()));
475
476 let result = rules.validate_request(&data).await;
477 assert!(result.is_ok());
478
479 let mut data = HashMap::new();
481 data.insert("password".to_string(), Value::String("secret123".to_string()));
482 data.insert("password_confirmation".to_string(), Value::String("different".to_string()));
483
484 let result = rules.validate_request(&data).await;
485 assert!(result.is_err());
486 }
487
488 #[tokio::test]
489 async fn test_rules_builder_required_string() {
490 let rules = RulesBuilder::new()
491 .required_string("name", Some(2), Some(50))
492 .build();
493
494 let result = rules.validate_field("name", &Value::String("John".to_string())).await;
496 assert!(result.is_ok());
497
498 let result = rules.validate_field("name", &Value::String("".to_string())).await;
500 assert!(result.is_err());
501
502 let result = rules.validate_field("name", &Value::String("J".to_string())).await;
504 assert!(result.is_err());
505 }
506
507 #[tokio::test]
508 async fn test_rules_builder_required_email() {
509 let rules = RulesBuilder::new()
510 .required_email("email")
511 .build();
512
513 let result = rules.validate_field("email", &Value::String("test@example.com".to_string())).await;
515 assert!(result.is_ok());
516
517 let result = rules.validate_field("email", &Value::String("".to_string())).await;
519 assert!(result.is_err());
520
521 let result = rules.validate_field("email", &Value::String("not-an-email".to_string())).await;
523 assert!(result.is_err());
524 }
525
526 #[tokio::test]
527 async fn test_rules_builder_optional_email() {
528 let rules = RulesBuilder::new()
529 .optional_email("email")
530 .build();
531
532 let result = rules.validate_field("email", &Value::String("test@example.com".to_string())).await;
534 assert!(result.is_ok());
535
536 let result = rules.validate_field("email", &Value::Null).await;
538 assert!(result.is_ok());
539
540 let result = rules.validate_field("email", &Value::String("not-an-email".to_string())).await;
542 assert!(result.is_err());
543 }
544
545 #[tokio::test]
546 async fn test_rules_builder_required_number() {
547 let rules = RulesBuilder::new()
548 .required_number("age", Some(0.0), Some(120.0))
549 .build();
550
551 let result = rules.validate_field("age", &Value::Number(serde_json::Number::from(25))).await;
553 assert!(result.is_ok());
554
555 let result = rules.validate_field("age", &Value::Null).await;
557 assert!(result.is_err());
558
559 let result = rules.validate_field("age", &Value::Number(serde_json::Number::from(150))).await;
561 assert!(result.is_err());
562 }
563
564 #[tokio::test]
565 async fn test_rules_builder_required_integer() {
566 let rules = RulesBuilder::new()
567 .required_integer("count", Some(1.0), Some(100.0))
568 .build();
569
570 let result = rules.validate_field("count", &Value::Number(serde_json::Number::from(10))).await;
572 assert!(result.is_ok());
573
574 let result = rules.validate_field("count", &Value::Number(serde_json::Number::from_f64(10.5).unwrap())).await;
576 assert!(result.is_err());
577 }
578
579 #[tokio::test]
580 async fn test_rules_builder_pattern() {
581 let rules = RulesBuilder::new()
582 .pattern("code", r"^[A-Z]{3}$")
583 .build();
584
585 let result = rules.validate_field("code", &Value::String("ABC".to_string())).await;
587 assert!(result.is_ok());
588
589 let result = rules.validate_field("code", &Value::String("abc".to_string())).await;
591 assert!(result.is_err());
592 }
593
594 #[tokio::test]
595 async fn test_rules_builder_one_of() {
596 let rules = RulesBuilder::new()
597 .one_of("status", vec!["active".to_string(), "inactive".to_string()])
598 .build();
599
600 let result = rules.validate_field("status", &Value::String("active".to_string())).await;
602 assert!(result.is_ok());
603
604 let result = rules.validate_field("status", &Value::String("unknown".to_string())).await;
606 assert!(result.is_err());
607 }
608
609 #[tokio::test]
610 async fn test_rules_combined_field_and_request_validation() {
611 let password_match_rule = CustomValidator::new("password_match", |value, _field| {
612 if let Some(obj) = value.as_object() {
613 let password = obj.get("password").and_then(|v| v.as_str());
614 let confirmation = obj.get("password_confirmation").and_then(|v| v.as_str());
615
616 if let (Some(pwd), Some(conf)) = (password, confirmation) {
617 if pwd == conf {
618 Ok(())
619 } else {
620 Err(ValidationError::new("password_confirmation", "Passwords do not match").into())
621 }
622 } else {
623 Ok(())
624 }
625 } else {
626 Ok(())
627 }
628 });
629
630 let rules = RulesBuilder::new()
631 .required_string("password", Some(8), None)
632 .required_string("password_confirmation", Some(8), None)
633 .request_rule(password_match_rule)
634 .build();
635
636 let mut data = HashMap::new();
637 data.insert("password".to_string(), Value::String("password123".to_string()));
638 data.insert("password_confirmation".to_string(), Value::String("password123".to_string()));
639
640 let result = rules.validate(&data).await;
642 assert!(result.is_ok());
643
644 let mut data = HashMap::new();
646 data.insert("password".to_string(), Value::String("password123".to_string()));
647 data.insert("password_confirmation".to_string(), Value::String("different".to_string()));
648
649 let result = rules.validate(&data).await;
650 assert!(result.is_err());
651
652 let errors = result.unwrap_err();
653 assert!(errors.has_field_errors("password_confirmation"));
654 }
655}