expect_json/expects/ops/
expect_email.rs1use crate::expect_op;
2use crate::Context;
3use crate::ExpectOp;
4use crate::ExpectOpError;
5use crate::ExpectOpResult;
6use crate::JsonType;
7use core::str::FromStr;
8use email_address::EmailAddress;
9
10#[expect_op(internal, name = "email")]
16#[derive(Debug, Clone, Default, PartialEq)]
17pub struct ExpectEmail {
18 expected_domain: Option<String>,
19 expected_local_part: Option<String>,
20}
21
22impl ExpectEmail {
23 pub(crate) fn new() -> Self {
24 Self {
25 expected_domain: None,
26 expected_local_part: None,
27 }
28 }
29
30 pub fn local_part<S>(mut self, local_part: S) -> Self
61 where
62 S: Into<String>,
63 {
64 self.expected_local_part = Some(local_part.into());
65 self
66 }
67
68 pub fn domain<S>(mut self, domain: S) -> Self
98 where
99 S: Into<String>,
100 {
101 self.expected_domain = Some(domain.into());
102 self
103 }
104}
105
106impl ExpectOp for ExpectEmail {
107 fn on_string(&self, context: &mut Context, received: &str) -> ExpectOpResult<()> {
108 let email = EmailAddress::from_str(received).map_err(|e| {
109 let error_message = format!("Invalid email address, received '{received}'");
110 ExpectOpError::custom_error(context, self, error_message, e)
111 })?;
112
113 if let Some(expected_local_part) = &self.expected_local_part {
114 if email.local_part() != expected_local_part {
115 return Err(ExpectOpError::custom(
116 context,
117 self,
118 format!(
119 "Local part mismatch, expected '{}', received '{received}'",
120 expected_local_part
121 ),
122 ));
123 }
124 }
125
126 if let Some(expected_domain) = &self.expected_domain {
127 if email.domain() != expected_domain {
128 return Err(ExpectOpError::custom(
129 context,
130 self,
131 format!(
132 "Domain mismatch, expected '{}', received '{received}'",
133 expected_domain
134 ),
135 ));
136 }
137 }
138
139 Ok(())
140 }
141
142 fn supported_types(&self) -> &'static [JsonType] {
143 &[JsonType::String]
144 }
145}
146
147#[cfg(test)]
148mod test_email {
149 use crate::expect;
150 use crate::expect_json_eq;
151 use pretty_assertions::assert_eq;
152 use serde_json::json;
153
154 #[test]
155 fn it_should_accept_valid_email() {
156 let left = json!("test@example.com");
157 let right = json!(expect::email());
158
159 let output = expect_json_eq(&left, &right);
160 assert!(output.is_ok());
161 }
162
163 #[test]
164 fn it_should_accept_valid_email_with_plus_sign() {
165 let left = json!("test+test@example.com");
166 let right = json!(expect::email());
167
168 let output = expect_json_eq(&left, &right);
169 assert!(output.is_ok());
170 }
171
172 #[test]
173 fn it_should_accept_email_inside_object() {
174 let left = json!({ "name": "Joe", "email": "test@example.com" });
175 let right = json!({
176 "name": "Joe",
177 "email": expect::email(),
178 });
179
180 let output = expect_json_eq(&left, &right);
181 assert!(output.is_ok());
182 }
183
184 #[test]
185 fn it_should_reject_invalid_email() {
186 let left = json!("🦊");
187 let right = json!(expect::email());
188
189 let output = expect_json_eq(&left, &right).unwrap_err().to_string();
190 assert_eq!(
191 output,
192 format!(
193 r#"Json expect::email() error at root:
194 Invalid email address, received '🦊',
195 Missing separator character '@'."#
196 )
197 );
198 }
199}
200
201#[cfg(test)]
202mod test_local_part {
203 use crate::expect;
204 use crate::expect_json_eq;
205 use pretty_assertions::assert_eq;
206 use serde_json::json;
207
208 #[test]
209 fn it_should_accept_valid_local_part() {
210 let left = json!("test@example.com");
211 let right = json!(expect::email().local_part("test"));
212
213 let output = expect_json_eq(&left, &right);
214 assert!(output.is_ok());
215 }
216
217 #[test]
218 fn it_should_reject_invalid_local_part() {
219 let left = json!("test@example.com");
220 let right = json!(expect::email().local_part("🦊"));
221
222 let output = expect_json_eq(&left, &right).unwrap_err().to_string();
223 assert_eq!(
224 output,
225 format!(
226 r#"Json expect::email() error at root:
227 Local part mismatch, expected '🦊', received 'test@example.com'"#
228 )
229 );
230 }
231}
232
233#[cfg(test)]
234mod test_domain {
235 use crate::expect;
236 use crate::expect_json_eq;
237 use pretty_assertions::assert_eq;
238 use serde_json::json;
239
240 #[test]
241 fn it_should_accept_valid_domain() {
242 let left = json!("test@example.com");
243 let right = json!(expect::email().domain("example.com"));
244
245 let output = expect_json_eq(&left, &right);
246 assert!(output.is_ok());
247 }
248
249 #[test]
250 fn it_should_reject_invalid_domain() {
251 let left = json!("test@example.com");
252 let right = json!(expect::email().domain("🦊.fox"));
253
254 let output = expect_json_eq(&left, &right).unwrap_err().to_string();
255 assert_eq!(
256 output,
257 format!(
258 r#"Json expect::email() error at root:
259 Domain mismatch, expected '🦊.fox', received 'test@example.com'"#
260 )
261 );
262 }
263}