Skip to main content

reinhardt_forms/fields/
email_field.rs

1//! Email field with validation
2
3use crate::field::{FieldError, FieldResult, FormField, Widget};
4use regex::Regex;
5
6/// Email field with format validation
7#[derive(Debug, Clone)]
8pub struct EmailField {
9	pub name: String,
10	pub label: Option<String>,
11	pub required: bool,
12	pub help_text: Option<String>,
13	pub widget: Widget,
14	pub initial: Option<serde_json::Value>,
15	pub max_length: Option<usize>,
16	pub min_length: Option<usize>,
17}
18
19impl EmailField {
20	/// Create a new EmailField with the given name
21	///
22	/// # Examples
23	///
24	/// ```
25	/// use reinhardt_forms::fields::EmailField;
26	///
27	/// let field = EmailField::new("email".to_string());
28	/// assert_eq!(field.name, "email");
29	/// assert!(!field.required);
30	/// assert_eq!(field.max_length, Some(320));
31	/// ```
32	pub fn new(name: String) -> Self {
33		Self {
34			name,
35			label: None,
36			required: false,
37			help_text: None,
38			widget: Widget::EmailInput,
39			initial: None,
40			max_length: Some(320), // RFC standard: 64 (local) + @ + 255 (domain)
41			min_length: None,
42		}
43	}
44
45	/// Set the field as required
46	///
47	/// # Examples
48	///
49	/// ```
50	/// use reinhardt_forms::fields::EmailField;
51	///
52	/// let field = EmailField::new("contact".to_string()).required();
53	/// assert!(field.required);
54	/// ```
55	pub fn required(mut self) -> Self {
56		self.required = true;
57		self
58	}
59
60	/// Set the maximum length for the field
61	///
62	/// # Examples
63	///
64	/// ```
65	/// use reinhardt_forms::fields::EmailField;
66	///
67	/// let field = EmailField::new("email".to_string()).with_max_length(100);
68	/// assert_eq!(field.max_length, Some(100));
69	/// ```
70	pub fn with_max_length(mut self, max_length: usize) -> Self {
71		self.max_length = Some(max_length);
72		self
73	}
74
75	/// Set the minimum length for the field
76	///
77	/// # Examples
78	///
79	/// ```
80	/// use reinhardt_forms::fields::EmailField;
81	///
82	/// let field = EmailField::new("email".to_string()).with_min_length(5);
83	/// assert_eq!(field.min_length, Some(5));
84	/// ```
85	pub fn with_min_length(mut self, min_length: usize) -> Self {
86		self.min_length = Some(min_length);
87		self
88	}
89
90	/// Set the label for the field
91	///
92	/// # Examples
93	///
94	/// ```
95	/// use reinhardt_forms::fields::EmailField;
96	///
97	/// let field = EmailField::new("contact_email".to_string()).with_label("Email Address");
98	/// assert_eq!(field.label, Some("Email Address".to_string()));
99	/// ```
100	pub fn with_label(mut self, label: impl Into<String>) -> Self {
101		self.label = Some(label.into());
102		self
103	}
104
105	/// Set the help text for the field
106	///
107	/// # Examples
108	///
109	/// ```
110	/// use reinhardt_forms::fields::EmailField;
111	///
112	/// let field = EmailField::new("email".to_string()).with_help_text("We'll never share your email");
113	/// assert_eq!(field.help_text, Some("We'll never share your email".to_string()));
114	/// ```
115	pub fn with_help_text(mut self, help_text: impl Into<String>) -> Self {
116		self.help_text = Some(help_text.into());
117		self
118	}
119
120	/// Set the initial value for the field
121	///
122	/// # Examples
123	///
124	/// ```
125	/// use reinhardt_forms::fields::EmailField;
126	///
127	/// let field = EmailField::new("email".to_string()).with_initial("user@example.com");
128	/// assert_eq!(field.initial, Some(serde_json::json!("user@example.com")));
129	/// ```
130	pub fn with_initial(mut self, initial: impl Into<String>) -> Self {
131		self.initial = Some(serde_json::json!(initial.into()));
132		self
133	}
134
135	/// Validate email format
136	fn validate_email(email: &str) -> bool {
137		// Basic email validation regex
138		// This is a simplified version - production should use a more robust validator
139		let email_regex = Regex::new(
140            r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$"
141        ).unwrap();
142
143		email_regex.is_match(email)
144	}
145}
146
147// Note: Default trait is not implemented because EmailField requires a name
148
149impl FormField for EmailField {
150	fn name(&self) -> &str {
151		&self.name
152	}
153
154	fn label(&self) -> Option<&str> {
155		self.label.as_deref()
156	}
157
158	fn required(&self) -> bool {
159		self.required
160	}
161
162	fn help_text(&self) -> Option<&str> {
163		self.help_text.as_deref()
164	}
165
166	fn widget(&self) -> &Widget {
167		&self.widget
168	}
169
170	fn initial(&self) -> Option<&serde_json::Value> {
171		self.initial.as_ref()
172	}
173
174	fn clean(&self, value: Option<&serde_json::Value>) -> FieldResult<serde_json::Value> {
175		match value {
176			None if self.required => Err(FieldError::Required(self.name.clone())),
177			None => Ok(serde_json::Value::String(String::new())),
178			Some(v) => {
179				let s = v
180					.as_str()
181					.ok_or_else(|| FieldError::Validation("Expected string".to_string()))?;
182
183				// Trim whitespace
184				let s = s.trim();
185
186				// Return empty string if not required and empty
187				if s.is_empty() {
188					if self.required {
189						return Err(FieldError::Required(self.name.clone()));
190					}
191					return Ok(serde_json::Value::String(String::new()));
192				}
193
194				// Check length constraints using character count (not byte count)
195				// for correct multi-byte character handling
196				let char_count = s.chars().count();
197				if let Some(max) = self.max_length
198					&& char_count > max
199				{
200					return Err(FieldError::Validation(format!(
201						"Ensure this value has at most {} characters (it has {})",
202						max, char_count
203					)));
204				}
205
206				if let Some(min) = self.min_length
207					&& char_count < min
208				{
209					return Err(FieldError::Validation(format!(
210						"Ensure this value has at least {} characters (it has {})",
211						min, char_count
212					)));
213				}
214
215				// Validate email format
216				if !Self::validate_email(s) {
217					return Err(FieldError::Validation(
218						"Enter a valid email address".to_string(),
219					));
220				}
221
222				Ok(serde_json::Value::String(s.to_string()))
223			}
224		}
225	}
226}