leptos_forms_rs/validation/
rules.rs1use crate::core::types::FieldValue;
7use regex::Regex;
8use serde::{Deserialize, Serialize};
9
10pub type FieldValidator = Box<dyn Fn(&FieldValue) -> Result<(), String> + Send + Sync>;
12
13#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
15pub enum Validator {
16 Required,
17 Email,
18 Url,
19 MinLength(usize),
20 MaxLength(usize),
21 Min(f64),
22 Max(f64),
23 Pattern(String),
24 Range(f64, f64),
25 Custom(String),
26}
27
28impl Validator {
29 pub fn required() -> Self {
31 Self::Required
32 }
33
34 pub fn email() -> Self {
36 Self::Email
37 }
38
39 pub fn url() -> Self {
41 Self::Url
42 }
43
44 pub fn min_length(min: usize) -> Self {
46 Self::MinLength(min)
47 }
48
49 pub fn max_length(max: usize) -> Self {
51 Self::MaxLength(max)
52 }
53
54 pub fn min(min: f64) -> Self {
56 Self::Min(min)
57 }
58
59 pub fn max(max: f64) -> Self {
61 Self::Max(max)
62 }
63
64 pub fn pattern(pattern: String) -> Self {
66 Self::Pattern(pattern)
67 }
68
69 pub fn range(min: f64, max: f64) -> Self {
71 Self::Range(min, max)
72 }
73
74 pub fn custom(name: String) -> Self {
76 Self::Custom(name)
77 }
78
79 pub fn validate(&self, value: &FieldValue) -> Result<(), String> {
81 match self {
82 Validator::Required => {
83 if value.is_empty() {
84 Err("This field is required".to_string())
85 } else {
86 Ok(())
87 }
88 }
89 Validator::Email => {
90 if let FieldValue::String(email) = value {
91 let email_regex = Regex::new(r"^[^\s@]+@[^\s@]+\.[^\s@]+$").unwrap();
92 if email_regex.is_match(email) {
93 Ok(())
94 } else {
95 Err("Invalid email format".to_string())
96 }
97 } else {
98 Err("Email must be a string".to_string())
99 }
100 }
101 Validator::Url => {
102 if let FieldValue::String(url) = value {
103 let url_regex = Regex::new(r"^https?://[^\s/$.?#].[^\s]*$").unwrap();
104 if url_regex.is_match(url) {
105 Ok(())
106 } else {
107 Err("Invalid URL format".to_string())
108 }
109 } else {
110 Err("URL must be a string".to_string())
111 }
112 }
113 Validator::MinLength(min_len) => {
114 if let FieldValue::String(s) = value {
115 if s.len() >= *min_len {
116 Ok(())
117 } else {
118 Err(format!("Minimum length is {} characters", min_len))
119 }
120 } else {
121 Err("Value must be a string".to_string())
122 }
123 }
124 Validator::MaxLength(max_len) => {
125 if let FieldValue::String(s) = value {
126 if s.len() <= *max_len {
127 Ok(())
128 } else {
129 Err(format!("Maximum length is {} characters", max_len))
130 }
131 } else {
132 Err("Value must be a string".to_string())
133 }
134 }
135 Validator::Min(min_val) => {
136 if let Some(num) = value.as_number() {
137 if num >= *min_val {
138 Ok(())
139 } else {
140 Err(format!("Minimum value is {}", min_val))
141 }
142 } else {
143 Err("Value must be a number".to_string())
144 }
145 }
146 Validator::Max(max_val) => {
147 if let Some(num) = value.as_number() {
148 if num <= *max_val {
149 Ok(())
150 } else {
151 Err(format!("Maximum value is {}", max_val))
152 }
153 } else {
154 Err("Value must be a number".to_string())
155 }
156 }
157 Validator::Pattern(pattern) => {
158 if let FieldValue::String(s) = value {
159 let regex = Regex::new(pattern).map_err(|_| "Invalid pattern".to_string())?;
160 if regex.is_match(s) {
161 Ok(())
162 } else {
163 Err("Value doesn't match required pattern".to_string())
164 }
165 } else {
166 Err("Value must be a string".to_string())
167 }
168 }
169 Validator::Range(min, max) => {
170 if let Some(num) = value.as_number() {
171 if num >= *min && num <= *max {
172 Ok(())
173 } else {
174 Err(format!("Value must be between {} and {}", min, max))
175 }
176 } else {
177 Err("Value must be a number".to_string())
178 }
179 }
180 Validator::Custom(_name) => {
181 Err("Custom validator not implemented".to_string())
183 }
184 }
185 }
186
187 pub fn description(&self) -> String {
189 match self {
190 Validator::Required => "Required field".to_string(),
191 Validator::Email => "Valid email address".to_string(),
192 Validator::Url => "Valid URL".to_string(),
193 Validator::MinLength(min) => format!("Minimum {} characters", min),
194 Validator::MaxLength(max) => format!("Maximum {} characters", max),
195 Validator::Min(min) => format!("Minimum value {}", min),
196 Validator::Max(max) => format!("Maximum value {}", max),
197 Validator::Pattern(pattern) => format!("Must match pattern: {}", pattern),
198 Validator::Range(min, max) => format!("Value between {} and {}", min, max),
199 Validator::Custom(name) => format!("Custom validation: {}", name),
200 }
201 }
202
203 pub fn is_custom(&self) -> bool {
205 matches!(self, Validator::Custom(_))
206 }
207
208 pub fn custom_name(&self) -> Option<&str> {
210 if let Validator::Custom(name) = self {
211 Some(name)
212 } else {
213 None
214 }
215 }
216}
217
218impl std::fmt::Display for Validator {
219 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
220 write!(f, "{}", self.description())
221 }
222}