scim_server/resource/value_objects/
name.rs

1//! Name value object for SCIM user name components.
2//!
3//! This module provides a type-safe wrapper around SCIM name attributes with built-in validation.
4//! Name attributes represent the components of a user's real name as defined in RFC 7643 Section 4.1.1.
5
6use 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/// A validated SCIM name attribute.
15///
16/// Name represents the components of a user's real name as defined in RFC 7643.
17/// It enforces validation rules at construction time, ensuring that only valid name
18/// attributes can exist in the system.
19///
20/// ## Validation Rules
21///
22/// - At least one name component must be provided (not all fields can be empty/None)
23/// - Individual name components cannot be empty strings
24/// - All fields are optional but if provided must contain meaningful content
25///
26/// ## Examples
27///
28/// ```rust
29/// use scim_server::resource::value_objects::Name;
30///
31/// fn main() -> Result<(), Box<dyn std::error::Error>> {
32///     // Create with full name components
33///     let name = Name::new(
34///         Some("Ms. Barbara J Jensen, III".to_string()),
35///         Some("Jensen".to_string()),
36///         Some("Barbara".to_string()),
37///         Some("Jane".to_string()),
38///         Some("Ms.".to_string()),
39///         Some("III".to_string())
40///     )?;
41///
42///     // Create with minimal components
43///     let simple_name = Name::new_simple(
44///         "John".to_string(),
45///         "Doe".to_string()
46///     )?;
47///
48///     Ok(())
49/// }
50/// ```
51#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
52pub struct Name {
53    pub formatted: Option<String>,
54    #[serde(rename = "familyName")]
55    pub family_name: Option<String>,
56    #[serde(rename = "givenName")]
57    pub given_name: Option<String>,
58    #[serde(rename = "middleName")]
59    pub middle_name: Option<String>,
60    #[serde(rename = "honorificPrefix")]
61    pub honorific_prefix: Option<String>,
62    #[serde(rename = "honorificSuffix")]
63    pub honorific_suffix: Option<String>,
64}
65
66impl Name {
67    /// Create a new Name with all components.
68    ///
69    /// This is the primary constructor that enforces all validation rules.
70    /// Use this method when creating Name instances from untrusted input.
71    ///
72    /// # Arguments
73    ///
74    /// * `formatted` - The full name, formatted for display
75    /// * `family_name` - The family name or last name
76    /// * `given_name` - The given name or first name
77    /// * `middle_name` - The middle name(s)
78    /// * `honorific_prefix` - The honorific prefix or title (e.g., "Ms.", "Dr.")
79    /// * `honorific_suffix` - The honorific suffix (e.g., "III", "Jr.")
80    ///
81    /// # Returns
82    ///
83    /// * `Ok(Name)` - If at least one field is provided and all provided fields are valid
84    /// * `Err(ValidationError)` - If all fields are None/empty or any field violates validation rules
85    pub fn new(
86        formatted: Option<String>,
87        family_name: Option<String>,
88        given_name: Option<String>,
89        middle_name: Option<String>,
90        honorific_prefix: Option<String>,
91        honorific_suffix: Option<String>,
92    ) -> ValidationResult<Self> {
93        // Validate individual components
94        if let Some(ref f) = formatted {
95            Self::validate_name_component(f, "formatted")?;
96        }
97        if let Some(ref fn_val) = family_name {
98            Self::validate_name_component(fn_val, "familyName")?;
99        }
100        if let Some(ref gn) = given_name {
101            Self::validate_name_component(gn, "givenName")?;
102        }
103        if let Some(ref mn) = middle_name {
104            Self::validate_name_component(mn, "middleName")?;
105        }
106        if let Some(ref hp) = honorific_prefix {
107            Self::validate_name_component(hp, "honorificPrefix")?;
108        }
109        if let Some(ref hs) = honorific_suffix {
110            Self::validate_name_component(hs, "honorificSuffix")?;
111        }
112
113        // Ensure at least one component is provided
114        if formatted.is_none()
115            && family_name.is_none()
116            && given_name.is_none()
117            && middle_name.is_none()
118            && honorific_prefix.is_none()
119            && honorific_suffix.is_none()
120        {
121            return Err(ValidationError::custom(
122                "At least one name component must be provided",
123            ));
124        }
125
126        Ok(Self {
127            formatted,
128            family_name,
129            given_name,
130            middle_name,
131            honorific_prefix,
132            honorific_suffix,
133        })
134    }
135
136    /// Create a simple Name with just given and family names.
137    ///
138    /// Convenience constructor for creating basic name structures.
139    ///
140    /// # Arguments
141    ///
142    /// * `given_name` - The given name or first name
143    /// * `family_name` - The family name or last name
144    ///
145    /// # Returns
146    ///
147    /// * `Ok(Name)` - If the names are valid
148    /// * `Err(ValidationError)` - If any name violates validation rules
149    pub fn new_simple(given_name: String, family_name: String) -> ValidationResult<Self> {
150        Self::new(None, Some(family_name), Some(given_name), None, None, None)
151    }
152
153    /// Create a Name with a formatted display name only.
154    ///
155    /// Convenience constructor for cases where only a formatted name is available.
156    ///
157    /// # Arguments
158    ///
159    /// * `formatted` - The full formatted name
160    ///
161    /// # Returns
162    ///
163    /// * `Ok(Name)` - If the formatted name is valid
164    /// * `Err(ValidationError)` - If the name violates validation rules
165    pub fn new_formatted(formatted: String) -> ValidationResult<Self> {
166        Self::new(Some(formatted), None, None, None, None, None)
167    }
168
169    /// Create a Name instance without validation for internal use.
170
171    /// Get the formatted name.
172    pub fn formatted(&self) -> Option<&str> {
173        self.formatted.as_deref()
174    }
175
176    /// Get the family name.
177    pub fn family_name(&self) -> Option<&str> {
178        self.family_name.as_deref()
179    }
180
181    /// Get the given name.
182    pub fn given_name(&self) -> Option<&str> {
183        self.given_name.as_deref()
184    }
185
186    /// Get the middle name.
187    pub fn middle_name(&self) -> Option<&str> {
188        self.middle_name.as_deref()
189    }
190
191    /// Get the honorific prefix.
192    pub fn honorific_prefix(&self) -> Option<&str> {
193        self.honorific_prefix.as_deref()
194    }
195
196    /// Get the honorific suffix.
197    pub fn honorific_suffix(&self) -> Option<&str> {
198        self.honorific_suffix.as_deref()
199    }
200
201    /// Generate a formatted display name from components.
202    ///
203    /// Creates a formatted name string from the available name components
204    /// if no explicit formatted name is provided.
205    ///
206    /// # Returns
207    ///
208    /// The formatted name if available, otherwise a constructed name from components,
209    /// or None if no components are available.
210    pub fn display_name(&self) -> Option<String> {
211        if let Some(ref formatted) = self.formatted {
212            return Some(formatted.clone());
213        }
214
215        let mut parts = Vec::new();
216
217        if let Some(ref prefix) = self.honorific_prefix {
218            parts.push(prefix.as_str());
219        }
220        if let Some(ref given) = self.given_name {
221            parts.push(given.as_str());
222        }
223        if let Some(ref middle) = self.middle_name {
224            parts.push(middle.as_str());
225        }
226        if let Some(ref family) = self.family_name {
227            parts.push(family.as_str());
228        }
229        if let Some(ref suffix) = self.honorific_suffix {
230            parts.push(suffix.as_str());
231        }
232
233        if parts.is_empty() {
234            None
235        } else {
236            Some(parts.join(" "))
237        }
238    }
239
240    /// Check if the name has any meaningful content.
241    pub fn is_empty(&self) -> bool {
242        self.formatted.is_none()
243            && self.family_name.is_none()
244            && self.given_name.is_none()
245            && self.middle_name.is_none()
246            && self.honorific_prefix.is_none()
247            && self.honorific_suffix.is_none()
248    }
249
250    /// Validate a name component.
251    fn validate_name_component(value: &str, field_name: &str) -> ValidationResult<()> {
252        if value.trim().is_empty() {
253            return Err(ValidationError::custom(format!(
254                "{}: Name component cannot be empty or contain only whitespace",
255                field_name
256            )));
257        }
258
259        // Check for reasonable length (SCIM doesn't specify but let's be practical)
260        if value.len() > 256 {
261            return Err(ValidationError::custom(format!(
262                "{}: Name component exceeds maximum length of 256 characters",
263                field_name
264            )));
265        }
266
267        // Check for control characters that shouldn't be in names
268        if value
269            .chars()
270            .any(|c| c.is_control() && c != '\n' && c != '\r' && c != '\t')
271        {
272            return Err(ValidationError::custom(format!(
273                "{}: Name component contains invalid control characters",
274                field_name
275            )));
276        }
277
278        Ok(())
279    }
280
281    /// Create a Name from a JSON value.
282    pub fn from_json(value: &Value) -> ValidationResult<Self> {
283        if let Value::Object(obj) = value {
284            let formatted = obj
285                .get("formatted")
286                .and_then(|v| v.as_str())
287                .map(|s| s.to_string());
288
289            let family_name = obj
290                .get("familyName")
291                .and_then(|v| v.as_str())
292                .map(|s| s.to_string());
293
294            let given_name = obj
295                .get("givenName")
296                .and_then(|v| v.as_str())
297                .map(|s| s.to_string());
298
299            let middle_name = obj
300                .get("middleName")
301                .and_then(|v| v.as_str())
302                .map(|s| s.to_string());
303
304            let honorific_prefix = obj
305                .get("honorificPrefix")
306                .and_then(|v| v.as_str())
307                .map(|s| s.to_string());
308
309            let honorific_suffix = obj
310                .get("honorificSuffix")
311                .and_then(|v| v.as_str())
312                .map(|s| s.to_string());
313
314            Self::new(
315                formatted,
316                family_name,
317                given_name,
318                middle_name,
319                honorific_prefix,
320                honorific_suffix,
321            )
322        } else {
323            Err(ValidationError::InvalidAttributeType {
324                attribute: "name".to_string(),
325                expected: "object".to_string(),
326                actual: "non-object".to_string(),
327            })
328        }
329    }
330}
331
332impl ValueObject for Name {
333    fn attribute_type(&self) -> AttributeType {
334        AttributeType::Complex
335    }
336
337    fn attribute_name(&self) -> &str {
338        "name"
339    }
340
341    fn to_json(&self) -> ValidationResult<Value> {
342        Ok(serde_json::to_value(self)?)
343    }
344
345    fn validate_against_schema(&self, definition: &AttributeDefinition) -> ValidationResult<()> {
346        if definition.data_type != AttributeType::Complex {
347            return Err(ValidationError::InvalidAttributeType {
348                attribute: definition.name.clone(),
349                expected: "complex".to_string(),
350                actual: format!("{:?}", definition.data_type),
351            });
352        }
353
354        if definition.name != "name" {
355            return Err(ValidationError::InvalidAttributeName {
356                actual: definition.name.clone(),
357                expected: "name".to_string(),
358            });
359        }
360
361        Ok(())
362    }
363
364    fn as_json_value(&self) -> Value {
365        serde_json::to_value(self).unwrap_or(Value::Null)
366    }
367
368    fn supports_definition(&self, definition: &AttributeDefinition) -> bool {
369        definition.data_type == AttributeType::Complex && definition.name == "name"
370    }
371
372    fn clone_boxed(&self) -> Box<dyn ValueObject> {
373        Box::new(self.clone())
374    }
375
376    fn as_any(&self) -> &dyn Any {
377        self
378    }
379}
380
381impl SchemaConstructible for Name {
382    fn from_schema_and_value(
383        definition: &AttributeDefinition,
384        value: &Value,
385    ) -> ValidationResult<Self> {
386        if definition.name != "name" || definition.data_type != AttributeType::Complex {
387            return Err(ValidationError::UnsupportedAttributeType {
388                attribute: definition.name.clone(),
389                type_name: format!("{:?}", definition.data_type),
390            });
391        }
392
393        Self::from_json(value)
394    }
395
396    fn can_construct_from(definition: &AttributeDefinition) -> bool {
397        definition.name == "name" && definition.data_type == AttributeType::Complex
398    }
399
400    fn constructor_priority() -> u8 {
401        100 // High priority for exact name match
402    }
403}
404
405impl fmt::Display for Name {
406    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
407        match self.display_name() {
408            Some(name) => write!(f, "{}", name),
409            None => write!(f, "[Empty Name]"),
410        }
411    }
412}
413
414#[cfg(test)]
415mod tests {
416    use super::*;
417
418    #[test]
419    fn test_valid_name_full() {
420        let name = Name::new(
421            Some("Ms. Barbara J Jensen, III".to_string()),
422            Some("Jensen".to_string()),
423            Some("Barbara".to_string()),
424            Some("Jane".to_string()),
425            Some("Ms.".to_string()),
426            Some("III".to_string()),
427        );
428
429        assert!(name.is_ok());
430        let name = name.unwrap();
431        assert_eq!(name.formatted(), Some("Ms. Barbara J Jensen, III"));
432        assert_eq!(name.family_name(), Some("Jensen"));
433        assert_eq!(name.given_name(), Some("Barbara"));
434        assert_eq!(name.middle_name(), Some("Jane"));
435        assert_eq!(name.honorific_prefix(), Some("Ms."));
436        assert_eq!(name.honorific_suffix(), Some("III"));
437    }
438
439    #[test]
440    fn test_valid_name_simple() {
441        let name = Name::new_simple("John".to_string(), "Doe".to_string());
442
443        assert!(name.is_ok());
444        let name = name.unwrap();
445        assert_eq!(name.given_name(), Some("John"));
446        assert_eq!(name.family_name(), Some("Doe"));
447        assert_eq!(name.formatted(), None);
448    }
449
450    #[test]
451    fn test_valid_name_formatted_only() {
452        let name = Name::new_formatted("John Doe".to_string());
453
454        assert!(name.is_ok());
455        let name = name.unwrap();
456        assert_eq!(name.formatted(), Some("John Doe"));
457        assert_eq!(name.given_name(), None);
458        assert_eq!(name.family_name(), None);
459    }
460
461    #[test]
462    fn test_empty_name_components() {
463        let result = Name::new(Some("".to_string()), None, None, None, None, None);
464        assert!(result.is_err());
465    }
466
467    #[test]
468    fn test_whitespace_only_components() {
469        let result = Name::new(None, Some("   ".to_string()), None, None, None, None);
470        assert!(result.is_err());
471    }
472
473    #[test]
474    fn test_all_none_components() {
475        let result = Name::new(None, None, None, None, None, None);
476        assert!(result.is_err());
477        assert!(
478            result
479                .unwrap_err()
480                .to_string()
481                .contains("At least one name component")
482        );
483    }
484
485    #[test]
486    fn test_too_long_component() {
487        let long_name = "a".repeat(300);
488        let result = Name::new_formatted(long_name);
489        assert!(result.is_err());
490        assert!(
491            result
492                .unwrap_err()
493                .to_string()
494                .contains("exceeds maximum length")
495        );
496    }
497
498    #[test]
499    fn test_control_characters() {
500        let result = Name::new_formatted("John\x00Doe".to_string());
501        assert!(result.is_err());
502        assert!(
503            result
504                .unwrap_err()
505                .to_string()
506                .contains("invalid control characters")
507        );
508    }
509
510    #[test]
511    fn test_display_name_with_formatted() {
512        let name = Name::new_formatted("Dr. John Smith Jr.".to_string()).unwrap();
513        assert_eq!(name.display_name(), Some("Dr. John Smith Jr.".to_string()));
514    }
515
516    #[test]
517    fn test_display_name_from_components() {
518        let name = Name::new(
519            None,
520            Some("Smith".to_string()),
521            Some("John".to_string()),
522            Some("Michael".to_string()),
523            Some("Dr.".to_string()),
524            Some("Jr.".to_string()),
525        )
526        .unwrap();
527
528        assert_eq!(
529            name.display_name(),
530            Some("Dr. John Michael Smith Jr.".to_string())
531        );
532    }
533
534    #[test]
535    fn test_display_name_partial_components() {
536        let name = Name::new(
537            None,
538            Some("Doe".to_string()),
539            Some("Jane".to_string()),
540            None,
541            None,
542            None,
543        )
544        .unwrap();
545
546        assert_eq!(name.display_name(), Some("Jane Doe".to_string()));
547    }
548
549    #[test]
550    fn test_display() {
551        let name = Name::new_simple("John".to_string(), "Doe".to_string()).unwrap();
552        assert_eq!(format!("{}", name), "John Doe");
553
554        // Test display with formatted name
555        let formatted_name = Name::new_formatted("Dr. John Smith Doe Jr.".to_string()).unwrap();
556        let display_str = format!("{}", formatted_name);
557        assert_eq!(display_str, "Dr. John Smith Doe Jr.");
558    }
559
560    #[test]
561    fn test_serialization() {
562        let name = Name::new(
563            Some("Ms. Barbara J Jensen, III".to_string()),
564            Some("Jensen".to_string()),
565            Some("Barbara".to_string()),
566            Some("Jane".to_string()),
567            Some("Ms.".to_string()),
568            Some("III".to_string()),
569        )
570        .unwrap();
571
572        let json = serde_json::to_string(&name).unwrap();
573        assert!(json.contains("\"formatted\":\"Ms. Barbara J Jensen, III\""));
574        assert!(json.contains("\"familyName\":\"Jensen\""));
575        assert!(json.contains("\"givenName\":\"Barbara\""));
576    }
577
578    #[test]
579    fn test_deserialization() {
580        let json = r#"{
581            "formatted": "Ms. Barbara J Jensen, III",
582            "familyName": "Jensen",
583            "givenName": "Barbara",
584            "middleName": "Jane",
585            "honorificPrefix": "Ms.",
586            "honorificSuffix": "III"
587        }"#;
588
589        let name: Name = serde_json::from_str(json).unwrap();
590        assert_eq!(name.formatted(), Some("Ms. Barbara J Jensen, III"));
591        assert_eq!(name.family_name(), Some("Jensen"));
592        assert_eq!(name.given_name(), Some("Barbara"));
593    }
594
595    #[test]
596    fn test_equality() {
597        let name1 = Name::new_simple("John".to_string(), "Doe".to_string()).unwrap();
598        let name2 = Name::new_simple("John".to_string(), "Doe".to_string()).unwrap();
599        let name3 = Name::new_simple("Jane".to_string(), "Doe".to_string()).unwrap();
600
601        assert_eq!(name1, name2);
602        assert_ne!(name1, name3);
603    }
604
605    #[test]
606    fn test_clone() {
607        let original = Name::new(
608            Some("Dr. John Smith".to_string()),
609            Some("Smith".to_string()),
610            Some("John".to_string()),
611            None,
612            Some("Dr.".to_string()),
613            None,
614        )
615        .unwrap();
616
617        let cloned = original.clone();
618        assert_eq!(original, cloned);
619        assert_eq!(cloned.formatted(), Some("Dr. John Smith"));
620        assert_eq!(cloned.family_name(), Some("Smith"));
621    }
622
623    #[test]
624    fn test_allows_newlines_in_formatted() {
625        let name = Name::new_formatted("John\nDoe".to_string());
626        assert!(name.is_ok());
627    }
628
629    #[test]
630    fn test_allows_tabs_in_formatted() {
631        let name = Name::new_formatted("John\tDoe".to_string());
632        assert!(name.is_ok());
633    }
634}