elif_validation/validators/
required.rs1use crate::error::{ValidationError, ValidationResult};
4use crate::traits::ValidationRule;
5use async_trait::async_trait;
6use serde_json::Value;
7
8#[derive(Debug, Clone)]
10pub struct RequiredValidator {
11 pub message: Option<String>,
13}
14
15impl RequiredValidator {
16 pub fn new() -> Self {
18 Self { message: None }
19 }
20
21 pub fn with_message(message: impl Into<String>) -> Self {
23 Self {
24 message: Some(message.into()),
25 }
26 }
27
28 fn is_empty(&self, value: &Value) -> bool {
30 match value {
31 Value::Null => true,
32 Value::String(s) => s.trim().is_empty(),
33 Value::Array(arr) => arr.is_empty(),
34 Value::Object(obj) => obj.is_empty(),
35 _ => false,
36 }
37 }
38}
39
40impl Default for RequiredValidator {
41 fn default() -> Self {
42 Self::new()
43 }
44}
45
46#[async_trait]
47impl ValidationRule for RequiredValidator {
48 async fn validate(&self, value: &Value, field: &str) -> ValidationResult<()> {
49 if self.is_empty(value) {
50 let message = self
51 .message.clone()
52 .unwrap_or_else(|| format!("{} is required", field));
53
54 Err(ValidationError::with_code(field, message, "required").into())
55 } else {
56 Ok(())
57 }
58 }
59
60 fn rule_name(&self) -> &'static str {
61 "required"
62 }
63
64 fn parameters(&self) -> Option<Value> {
65 self.message.as_ref().map(|msg| {
66 serde_json::json!({
67 "message": msg
68 })
69 })
70 }
71}
72
73#[cfg(test)]
74mod tests {
75 use super::*;
76
77 #[tokio::test]
78 async fn test_required_validator_with_null() {
79 let validator = RequiredValidator::new();
80 let result = validator.validate(&Value::Null, "email").await;
81
82 assert!(result.is_err());
83 let errors = result.unwrap_err();
84 assert!(errors.has_field_errors("email"));
85 }
86
87 #[tokio::test]
88 async fn test_required_validator_with_empty_string() {
89 let validator = RequiredValidator::new();
90 let result = validator.validate(&Value::String("".to_string()), "name").await;
91
92 assert!(result.is_err());
93 }
94
95 #[tokio::test]
96 async fn test_required_validator_with_whitespace_string() {
97 let validator = RequiredValidator::new();
98 let result = validator.validate(&Value::String(" ".to_string()), "name").await;
99
100 assert!(result.is_err());
101 }
102
103 #[tokio::test]
104 async fn test_required_validator_with_valid_string() {
105 let validator = RequiredValidator::new();
106 let result = validator.validate(&Value::String("John".to_string()), "name").await;
107
108 assert!(result.is_ok());
109 }
110
111 #[tokio::test]
112 async fn test_required_validator_with_empty_array() {
113 let validator = RequiredValidator::new();
114 let result = validator.validate(&Value::Array(vec![]), "tags").await;
115
116 assert!(result.is_err());
117 }
118
119 #[tokio::test]
120 async fn test_required_validator_with_filled_array() {
121 let validator = RequiredValidator::new();
122 let result = validator.validate(
123 &Value::Array(vec![Value::String("tag1".to_string())]),
124 "tags"
125 ).await;
126
127 assert!(result.is_ok());
128 }
129
130 #[tokio::test]
131 async fn test_required_validator_with_custom_message() {
132 let validator = RequiredValidator::with_message("This field cannot be empty");
133 let result = validator.validate(&Value::Null, "email").await;
134
135 assert!(result.is_err());
136 let errors = result.unwrap_err();
137 let field_errors = errors.get_field_errors("email").unwrap();
138 assert_eq!(field_errors[0].message, "This field cannot be empty");
139 }
140
141 #[tokio::test]
142 async fn test_required_validator_with_numbers() {
143 let validator = RequiredValidator::new();
144
145 assert!(validator.validate(&Value::Number(serde_json::Number::from(0)), "count").await.is_ok());
147 assert!(validator.validate(&Value::Number(serde_json::Number::from(42)), "count").await.is_ok());
148 }
149
150 #[tokio::test]
151 async fn test_required_validator_with_boolean() {
152 let validator = RequiredValidator::new();
153
154 assert!(validator.validate(&Value::Bool(false), "active").await.is_ok());
156 assert!(validator.validate(&Value::Bool(true), "active").await.is_ok());
157 }
158}