scim_server/resource/value_objects/
email_address.rs1use crate::error::{ValidationError, ValidationResult};
7use crate::resource::value_objects::value_object_trait::{SchemaConstructible, ValueObject};
8use crate::schema::types::{AttributeDefinition, AttributeType};
9use serde::{Deserialize, Serialize};
10use serde_json::Value;
11use std::any::Any;
12use std::fmt;
13
14#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
50pub struct EmailAddress {
51 pub value: String,
52 #[serde(rename = "type")]
53 pub email_type: Option<String>,
54 pub primary: Option<bool>,
55 pub display: Option<String>,
56}
57
58impl EmailAddress {
59 pub fn new(
76 value: String,
77 email_type: Option<String>,
78 primary: Option<bool>,
79 display: Option<String>,
80 ) -> ValidationResult<Self> {
81 Self::validate_value(&value)?;
82 if let Some(ref type_val) = email_type {
83 Self::validate_type(type_val)?;
84 }
85 if let Some(ref display_val) = display {
86 Self::validate_display(display_val)?;
87 }
88
89 Ok(Self {
90 value,
91 email_type,
92 primary,
93 display,
94 })
95 }
96
97 pub fn new_simple(value: String) -> ValidationResult<Self> {
110 Self::new(value, None, None, None)
111 }
112
113 pub fn value(&self) -> &str {
115 &self.value
116 }
117
118 pub fn email_type(&self) -> Option<&str> {
120 self.email_type.as_deref()
121 }
122
123 pub fn primary(&self) -> Option<bool> {
125 self.primary
126 }
127
128 pub fn display(&self) -> Option<&str> {
130 self.display.as_deref()
131 }
132
133 pub fn is_primary(&self) -> bool {
135 self.primary.unwrap_or(false)
136 }
137
138 fn validate_value(value: &str) -> ValidationResult<()> {
140 if value.is_empty() {
141 return Err(ValidationError::MissingRequiredSubAttribute {
142 attribute: "emails".to_string(),
143 sub_attribute: "value".to_string(),
144 });
145 }
146
147 Ok(())
156 }
157
158 fn validate_type(email_type: &str) -> ValidationResult<()> {
160 if email_type.is_empty() {
161 return Err(ValidationError::InvalidStringFormat {
162 attribute: "emails.type".to_string(),
163 details: "Email type cannot be empty".to_string(),
164 });
165 }
166
167 Ok(())
171 }
172
173 fn validate_display(_display: &str) -> ValidationResult<()> {
175 Ok(())
181 }
182}
183
184impl fmt::Display for EmailAddress {
185 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186 if let Some(ref display) = self.display {
187 write!(f, "{} <{}>", display, self.value)
188 } else {
189 write!(f, "{}", self.value)
190 }
191 }
192}
193
194impl TryFrom<String> for EmailAddress {
195 type Error = ValidationError;
196
197 fn try_from(value: String) -> ValidationResult<Self> {
198 Self::new_simple(value)
199 }
200}
201
202impl TryFrom<&str> for EmailAddress {
203 type Error = ValidationError;
204
205 fn try_from(value: &str) -> ValidationResult<Self> {
206 Self::new_simple(value.to_string())
207 }
208}
209
210impl ValueObject for EmailAddress {
211 fn attribute_type(&self) -> AttributeType {
212 AttributeType::Complex
213 }
214
215 fn attribute_name(&self) -> &str {
216 "emails"
217 }
218
219 fn to_json(&self) -> ValidationResult<Value> {
220 Ok(serde_json::to_value(self)?)
221 }
222
223 fn validate_against_schema(&self, definition: &AttributeDefinition) -> ValidationResult<()> {
224 if definition.data_type != AttributeType::Complex {
225 return Err(ValidationError::InvalidAttributeType {
226 attribute: definition.name.clone(),
227 expected: "complex".to_string(),
228 actual: format!("{:?}", definition.data_type),
229 });
230 }
231
232 if definition.name != "emails" && definition.name != "value" {
234 return Err(ValidationError::InvalidAttributeName {
235 actual: definition.name.clone(),
236 expected: "emails or value".to_string(),
237 });
238 }
239
240 Ok(())
241 }
242
243 fn as_json_value(&self) -> Value {
244 serde_json::to_value(self).unwrap_or(Value::Null)
245 }
246
247 fn supports_definition(&self, definition: &AttributeDefinition) -> bool {
248 definition.data_type == AttributeType::Complex
249 && (definition.name == "emails" || definition.name == "value")
250 }
251
252 fn clone_boxed(&self) -> Box<dyn ValueObject> {
253 Box::new(self.clone())
254 }
255
256 fn as_any(&self) -> &dyn Any {
257 self
258 }
259}
260
261impl SchemaConstructible for EmailAddress {
262 fn from_schema_and_value(
263 definition: &AttributeDefinition,
264 value: &Value,
265 ) -> ValidationResult<Self> {
266 if definition.data_type != AttributeType::Complex {
267 return Err(ValidationError::UnsupportedAttributeType {
268 attribute: definition.name.clone(),
269 type_name: format!("{:?}", definition.data_type),
270 });
271 }
272
273 if let Value::Object(obj) = value {
274 let email_value = obj.get("value").and_then(|v| v.as_str()).ok_or_else(|| {
275 ValidationError::InvalidAttributeType {
276 attribute: definition.name.clone(),
277 expected: "object with 'value' field".to_string(),
278 actual: "object without 'value' field".to_string(),
279 }
280 })?;
281
282 let email_type = obj
283 .get("type")
284 .and_then(|v| v.as_str())
285 .map(|s| s.to_string());
286
287 let primary = obj.get("primary").and_then(|v| v.as_bool());
288
289 let display = obj
290 .get("display")
291 .and_then(|v| v.as_str())
292 .map(|s| s.to_string());
293
294 Self::new(email_value.to_string(), email_type, primary, display)
295 } else if let Some(email_str) = value.as_str() {
296 Self::new(email_str.to_string(), None, None, None)
298 } else {
299 Err(ValidationError::InvalidAttributeType {
300 attribute: definition.name.clone(),
301 expected: "object or string".to_string(),
302 actual: "neither object nor string".to_string(),
303 })
304 }
305 }
306
307 fn can_construct_from(definition: &AttributeDefinition) -> bool {
308 definition.data_type == AttributeType::Complex
309 && (definition.name == "emails"
310 || definition.name == "value"
311 || definition.name.contains("email"))
312 }
313
314 fn constructor_priority() -> u8 {
315 80 }
317}
318
319#[cfg(test)]
320mod tests {
321 use super::*;
322 use serde_json;
323
324 #[test]
325 fn test_valid_email_full() {
326 let email = EmailAddress::new(
327 "bjensen@example.com".to_string(),
328 Some("work".to_string()),
329 Some(true),
330 Some("Barbara Jensen".to_string()),
331 );
332 assert!(email.is_ok());
333
334 let email = email.unwrap();
335 assert_eq!(email.value(), "bjensen@example.com");
336 assert_eq!(email.email_type(), Some("work"));
337 assert_eq!(email.primary(), Some(true));
338 assert_eq!(email.display(), Some("Barbara Jensen"));
339 assert!(email.is_primary());
340 }
341
342 #[test]
343 fn test_valid_email_simple() {
344 let email = EmailAddress::new_simple("user@example.com".to_string());
345 assert!(email.is_ok());
346
347 let email = email.unwrap();
348 assert_eq!(email.value(), "user@example.com");
349 assert_eq!(email.email_type(), None);
350 assert_eq!(email.primary(), None);
351 assert_eq!(email.display(), None);
352 assert!(!email.is_primary());
353 }
354
355 #[test]
356 fn test_empty_email_value() {
357 let result = EmailAddress::new_simple("".to_string());
358 assert!(result.is_err());
359
360 match result.unwrap_err() {
361 ValidationError::MissingRequiredSubAttribute {
362 attribute,
363 sub_attribute,
364 } => {
365 assert_eq!(attribute, "emails");
366 assert_eq!(sub_attribute, "value");
367 }
368 other => panic!(
369 "Expected MissingRequiredSubAttribute error, got: {:?}",
370 other
371 ),
372 }
373 }
374
375 #[test]
376 fn test_empty_email_type() {
377 let result = EmailAddress::new(
378 "test@example.com".to_string(),
379 Some("".to_string()),
380 None,
381 None,
382 );
383 assert!(result.is_err());
384
385 match result.unwrap_err() {
386 ValidationError::InvalidStringFormat { attribute, details } => {
387 assert_eq!(attribute, "emails.type");
388 assert!(details.contains("cannot be empty"));
389 }
390 other => panic!("Expected InvalidStringFormat error, got: {:?}", other),
391 }
392 }
393
394 #[test]
395 fn test_display() {
396 let email_with_display = EmailAddress::new(
397 "test@example.com".to_string(),
398 None,
399 None,
400 Some("Test User".to_string()),
401 )
402 .unwrap();
403 assert_eq!(
404 format!("{}", email_with_display),
405 "Test User <test@example.com>"
406 );
407
408 let email_without_display =
409 EmailAddress::new_simple("test@example.com".to_string()).unwrap();
410 assert_eq!(format!("{}", email_without_display), "test@example.com");
411 }
412
413 #[test]
414 fn test_serialization() {
415 let email = EmailAddress::new(
416 "serialize@example.com".to_string(),
417 Some("work".to_string()),
418 Some(true),
419 Some("Serialize Test".to_string()),
420 )
421 .unwrap();
422
423 let json = serde_json::to_string(&email).unwrap();
424 let expected = r#"{"value":"serialize@example.com","type":"work","primary":true,"display":"Serialize Test"}"#;
425 assert_eq!(json, expected);
426 }
427
428 #[test]
429 fn test_deserialization() {
430 let json = r#"{"value":"deserialize@example.com","type":"home","primary":false}"#;
431 let email: EmailAddress = serde_json::from_str(json).unwrap();
432
433 assert_eq!(email.value(), "deserialize@example.com");
434 assert_eq!(email.email_type(), Some("home"));
435 assert_eq!(email.primary(), Some(false));
436 assert_eq!(email.display(), None);
437 }
438
439 #[test]
440 fn test_try_from_string() {
441 let result = EmailAddress::try_from("try-from@example.com".to_string());
442 assert!(result.is_ok());
443 assert_eq!(result.unwrap().value(), "try-from@example.com");
444
445 let empty_result = EmailAddress::try_from("".to_string());
446 assert!(empty_result.is_err());
447 }
448
449 #[test]
450 fn test_try_from_str() {
451 let result = EmailAddress::try_from("try-from-str@example.com");
452 assert!(result.is_ok());
453 assert_eq!(result.unwrap().value(), "try-from-str@example.com");
454
455 let empty_result = EmailAddress::try_from("");
456 assert!(empty_result.is_err());
457 }
458
459 #[test]
460 fn test_equality() {
461 let email1 = EmailAddress::new(
462 "same@example.com".to_string(),
463 Some("work".to_string()),
464 Some(true),
465 None,
466 )
467 .unwrap();
468 let email2 = EmailAddress::new(
469 "same@example.com".to_string(),
470 Some("work".to_string()),
471 Some(true),
472 None,
473 )
474 .unwrap();
475 let email3 = EmailAddress::new_simple("different@example.com".to_string()).unwrap();
476
477 assert_eq!(email1, email2);
478 assert_ne!(email1, email3);
479 }
480
481 #[test]
482 fn test_hash() {
483 use std::collections::HashMap;
484
485 let email1 = EmailAddress::new_simple("hash-test-1@example.com".to_string()).unwrap();
486 let email2 = EmailAddress::new_simple("hash-test-2@example.com".to_string()).unwrap();
487
488 let mut map = HashMap::new();
489 map.insert(email1.clone(), "value1");
490 map.insert(email2.clone(), "value2");
491
492 assert_eq!(map.get(&email1), Some(&"value1"));
493 assert_eq!(map.get(&email2), Some(&"value2"));
494 }
495
496 #[test]
497 fn test_clone() {
498 let email = EmailAddress::new(
499 "clone@example.com".to_string(),
500 Some("work".to_string()),
501 Some(true),
502 Some("Clone Test".to_string()),
503 )
504 .unwrap();
505 let cloned = email.clone();
506
507 assert_eq!(email, cloned);
508 assert_eq!(email.value(), cloned.value());
509 assert_eq!(email.email_type(), cloned.email_type());
510 assert_eq!(email.primary(), cloned.primary());
511 assert_eq!(email.display(), cloned.display());
512 }
513}