scim_server/resource/value_objects/multi_valued.rs
1//! Multi-valued attribute container for SCIM resources.
2//!
3//! This module provides a generic container for handling multi-valued attributes
4//! in SCIM resources, with support for primary value tracking and validation.
5//!
6//! ## Design Principles
7//!
8//! - **Type Safety**: Generic over the contained value type
9//! - **Primary Constraint**: Ensures at most one primary value exists
10//! - **Immutable Operations**: Most operations return new instances
11//! - **SCIM Compliance**: Follows SCIM 2.0 multi-valued attribute patterns
12//!
13//! ## Usage Pattern
14//!
15//! ```rust
16//! use scim_server::resource::value_objects::{MultiValuedAttribute, EmailAddress};
17//!
18//! fn main() -> Result<(), Box<dyn std::error::Error>> {
19//! // Create multi-valued email addresses
20//! let emails = vec![
21//! EmailAddress::new_simple("work@example.com".to_string())?,
22//! EmailAddress::new_simple("personal@example.com".to_string())?,
23//! ];
24//!
25//! let multi_emails = MultiValuedAttribute::new(emails)?;
26//!
27//! // Set primary email
28//! let with_primary = multi_emails.with_primary(0)?;
29//!
30//! // Access primary email
31//! if let Some(primary) = with_primary.primary() {
32//! println!("Primary email: {}", primary.value());
33//! }
34//! Ok(())
35//! }
36//! ```
37
38use crate::error::{ValidationError, ValidationResult};
39use serde::{Deserialize, Serialize};
40use std::fmt;
41
42/// A generic container for multi-valued SCIM attributes.
43///
44/// This type provides type-safe handling of multi-valued attributes with
45/// support for designating one value as primary. It enforces the SCIM
46/// constraint that at most one value can be marked as primary.
47///
48/// # Type Parameters
49///
50/// * `T` - The type of values contained in the multi-valued attribute
51///
52/// # Examples
53///
54/// ```rust
55/// use scim_server::resource::value_objects::{MultiValuedAttribute, EmailAddress};
56///
57/// fn main() -> Result<(), Box<dyn std::error::Error>> {
58/// let emails = vec![
59/// EmailAddress::new_simple("work@example.com".to_string())?,
60/// EmailAddress::new_simple("personal@example.com".to_string())?,
61/// ];
62///
63/// let multi_emails = MultiValuedAttribute::new(emails)?;
64/// assert_eq!(multi_emails.len(), 2);
65/// assert!(multi_emails.primary().is_none());
66/// Ok(())
67/// }
68/// ```
69#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
70pub struct MultiValuedAttribute<T> {
71 /// The collection of values
72 values: Vec<T>,
73 /// Index of the primary value, if any
74 primary_index: Option<usize>,
75}
76
77impl<T> MultiValuedAttribute<T> {
78 /// Creates a new multi-valued attribute from a collection of values.
79 ///
80 /// # Arguments
81 ///
82 /// * `values` - Vector of values to store
83 ///
84 /// # Returns
85 ///
86 /// * `Ok(MultiValuedAttribute<T>)` - Successfully created multi-valued attribute
87 /// * `Err(ValidationError)` - If the input is invalid (e.g., empty vector)
88 ///
89 /// # Examples
90 ///
91 /// ```rust
92 /// use scim_server::resource::value_objects::{MultiValuedAttribute, EmailAddress};
93 ///
94 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
95 /// let emails = vec![
96 /// EmailAddress::new_simple("work@example.com".to_string())?,
97 /// EmailAddress::new_simple("personal@example.com".to_string())?,
98 /// ];
99 /// let multi_emails = MultiValuedAttribute::new(emails)?;
100 /// Ok(())
101 /// }
102 /// ```
103 pub fn new(values: Vec<T>) -> ValidationResult<Self> {
104 if values.is_empty() {
105 return Err(ValidationError::custom(
106 "Multi-valued attribute cannot be empty",
107 ));
108 }
109
110 Ok(Self {
111 values,
112 primary_index: None,
113 })
114 }
115
116 /// Creates a new multi-valued attribute with a single value.
117 ///
118 /// # Arguments
119 ///
120 /// * `value` - Single value to store
121 ///
122 /// # Returns
123 ///
124 /// A multi-valued attribute containing the single value
125 ///
126 /// # Examples
127 ///
128 /// ```rust
129 /// use scim_server::resource::value_objects::{MultiValuedAttribute, EmailAddress};
130 ///
131 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
132 /// let email = EmailAddress::new_simple("user@example.com".to_string())?;
133 /// let multi_email = MultiValuedAttribute::single(email);
134 /// assert_eq!(multi_email.len(), 1);
135 /// Ok(())
136 /// }
137 /// ```
138 pub fn single(value: T) -> Self {
139 Self {
140 values: vec![value],
141 primary_index: None,
142 }
143 }
144
145 /// Creates a new multi-valued attribute with a single primary value.
146 ///
147 /// # Arguments
148 ///
149 /// * `value` - Single value to store as primary
150 ///
151 /// # Returns
152 ///
153 /// A multi-valued attribute containing the single primary value
154 ///
155 /// # Examples
156 ///
157 /// ```rust
158 /// use scim_server::resource::value_objects::{MultiValuedAttribute, EmailAddress};
159 ///
160 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
161 /// let email = EmailAddress::new_simple("primary@example.com".to_string())?;
162 /// let multi_email = MultiValuedAttribute::single_primary(email);
163 /// assert!(multi_email.primary().is_some());
164 /// Ok(())
165 /// }
166 /// ```
167 pub fn single_primary(value: T) -> Self {
168 Self {
169 values: vec![value],
170 primary_index: Some(0),
171 }
172 }
173
174 /// Creates an empty multi-valued attribute for internal use.
175 ///
176 /// This method bypasses validation and is intended for internal use
177 /// where empty collections are temporarily needed during construction.
178 ///
179 /// # Returns
180 ///
181 /// An empty multi-valued attribute
182 pub(crate) fn empty() -> Self {
183 Self {
184 values: Vec::new(),
185 primary_index: None,
186 }
187 }
188
189 /// Returns the number of values in the collection.
190 ///
191 /// # Examples
192 ///
193 /// ```rust
194 /// use scim_server::resource::value_objects::{MultiValuedAttribute, EmailAddress};
195 ///
196 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
197 /// let emails = vec![
198 /// EmailAddress::new_simple("work@example.com".to_string())?,
199 /// EmailAddress::new_simple("personal@example.com".to_string())?,
200 /// ];
201 /// let multi_attr = MultiValuedAttribute::new(emails)?;
202 /// assert_eq!(multi_attr.len(), 2);
203 /// Ok(())
204 /// }
205 /// ```
206 pub fn len(&self) -> usize {
207 self.values.len()
208 }
209
210 /// Returns true if the collection is empty.
211 ///
212 /// # Examples
213 ///
214 /// ```rust
215 /// use scim_server::resource::value_objects::{MultiValuedAttribute, EmailAddress};
216 ///
217 /// let multi_attr = MultiValuedAttribute::single(
218 /// EmailAddress::new_simple("test@example.com".to_string()).unwrap()
219 /// );
220 /// assert!(!multi_attr.is_empty());
221 /// ```
222 pub fn is_empty(&self) -> bool {
223 self.values.is_empty()
224 }
225
226 /// Returns a reference to the primary value, if one is set.
227 ///
228 /// # Returns
229 ///
230 /// * `Some(&T)` - Reference to the primary value
231 /// * `None` - No primary value is set
232 ///
233 /// # Examples
234 ///
235 /// ```rust
236 /// use scim_server::resource::value_objects::{MultiValuedAttribute, EmailAddress};
237 ///
238 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
239 /// let emails = vec![
240 /// EmailAddress::new_simple("work@example.com".to_string())?,
241 /// EmailAddress::new_simple("personal@example.com".to_string())?,
242 /// ];
243 /// let multi_attr = MultiValuedAttribute::new(emails)?.with_primary(0)?;
244 /// if let Some(primary) = multi_attr.primary() {
245 /// println!("Primary value found");
246 /// }
247 /// Ok(())
248 /// }
249 /// ```
250 pub fn primary(&self) -> Option<&T> {
251 self.primary_index.and_then(|index| self.values.get(index))
252 }
253
254 /// Returns the index of the primary value, if one is set.
255 ///
256 /// # Returns
257 ///
258 /// * `Some(usize)` - Index of the primary value
259 /// * `None` - No primary value is set
260 pub fn primary_index(&self) -> Option<usize> {
261 self.primary_index
262 }
263
264 /// Returns a reference to all values in the collection.
265 ///
266 /// # Examples
267 ///
268 /// ```rust
269 /// use scim_server::resource::value_objects::{MultiValuedAttribute, EmailAddress};
270 ///
271 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
272 /// let emails = vec![
273 /// EmailAddress::new_simple("work@example.com".to_string())?,
274 /// EmailAddress::new_simple("personal@example.com".to_string())?,
275 /// ];
276 /// let multi_attr = MultiValuedAttribute::new(emails)?;
277 /// for value in multi_attr.values() {
278 /// println!("Value: {:?}", value);
279 /// }
280 /// Ok(())
281 /// }
282 /// ```
283 pub fn values(&self) -> &[T] {
284 &self.values
285 }
286
287 /// Returns a reference to the value at the specified index.
288 ///
289 /// # Arguments
290 ///
291 /// * `index` - Index of the value to retrieve
292 ///
293 /// # Returns
294 ///
295 /// * `Some(&T)` - Reference to the value at the index
296 /// * `None` - Index is out of bounds
297 pub fn get(&self, index: usize) -> Option<&T> {
298 self.values.get(index)
299 }
300
301 /// Creates a new multi-valued attribute with the specified value set as primary.
302 ///
303 /// # Arguments
304 ///
305 /// * `index` - Index of the value to set as primary
306 ///
307 /// # Returns
308 ///
309 /// * `Ok(MultiValuedAttribute<T>)` - New instance with primary value set
310 /// * `Err(ValidationError)` - If the index is out of bounds
311 ///
312 /// # Examples
313 ///
314 /// ```rust
315 /// use scim_server::resource::value_objects::{MultiValuedAttribute, EmailAddress};
316 ///
317 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
318 /// let emails = vec![
319 /// EmailAddress::new_simple("work@example.com".to_string())?,
320 /// EmailAddress::new_simple("personal@example.com".to_string())?,
321 /// ];
322 /// let multi_attr = MultiValuedAttribute::new(emails)?;
323 /// let with_primary = multi_attr.with_primary(0)?;
324 /// assert!(with_primary.primary().is_some());
325 /// Ok(())
326 /// }
327 /// ```
328 pub fn with_primary(mut self, index: usize) -> ValidationResult<Self> {
329 if index >= self.values.len() {
330 return Err(ValidationError::custom(format!(
331 "Primary index {} is out of bounds for collection of size {}",
332 index,
333 self.values.len()
334 )));
335 }
336
337 self.primary_index = Some(index);
338 Ok(self)
339 }
340
341 /// Creates a new multi-valued attribute with no primary value set.
342 ///
343 /// # Examples
344 ///
345 /// ```rust
346 /// use scim_server::resource::value_objects::{MultiValuedAttribute, EmailAddress};
347 ///
348 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
349 /// let emails = vec![
350 /// EmailAddress::new_simple("work@example.com".to_string())?,
351 /// EmailAddress::new_simple("personal@example.com".to_string())?,
352 /// ];
353 /// let multi_attr = MultiValuedAttribute::new(emails)?.with_primary(0)?;
354 /// let without_primary = multi_attr.without_primary();
355 /// assert!(without_primary.primary().is_none());
356 /// Ok(())
357 /// }
358 /// ```
359 pub fn without_primary(mut self) -> Self {
360 self.primary_index = None;
361 self
362 }
363
364 /// Creates a new multi-valued attribute with an additional value.
365 ///
366 /// # Arguments
367 ///
368 /// * `value` - Value to add to the collection
369 ///
370 /// # Returns
371 ///
372 /// A new multi-valued attribute with the added value
373 ///
374 /// # Examples
375 ///
376 /// ```rust
377 /// use scim_server::resource::value_objects::{MultiValuedAttribute, EmailAddress};
378 ///
379 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
380 /// let emails = vec![
381 /// EmailAddress::new_simple("work@example.com".to_string())?,
382 /// ];
383 /// let multi_attr = MultiValuedAttribute::new(emails)?;
384 /// let new_email = EmailAddress::new_simple("personal@example.com".to_string())?;
385 /// let with_value = multi_attr.with_value(new_email);
386 /// assert_eq!(with_value.len(), 2);
387 /// Ok(())
388 /// }
389 /// ```
390 pub fn with_value(mut self, value: T) -> Self {
391 self.values.push(value);
392 self
393 }
394
395 /// Creates a new multi-valued attribute with an additional primary value.
396 ///
397 /// This method adds the value and sets it as the primary value.
398 ///
399 /// # Arguments
400 ///
401 /// * `value` - Value to add as primary
402 ///
403 /// # Returns
404 ///
405 /// A new multi-valued attribute with the added primary value
406 ///
407 /// # Examples
408 ///
409 /// ```rust
410 /// use scim_server::resource::value_objects::{MultiValuedAttribute, EmailAddress};
411 ///
412 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
413 /// let emails = vec![
414 /// EmailAddress::new_simple("work@example.com".to_string())?,
415 /// ];
416 /// let multi_attr = MultiValuedAttribute::new(emails)?;
417 /// let new_email = EmailAddress::new_simple("primary@example.com".to_string())?;
418 /// let with_primary = multi_attr.with_primary_value(new_email.clone());
419 /// assert_eq!(with_primary.primary(), Some(&new_email));
420 /// Ok(())
421 /// }
422 /// ```
423 pub fn with_primary_value(mut self, value: T) -> Self {
424 self.values.push(value);
425 self.primary_index = Some(self.values.len() - 1);
426 self
427 }
428
429 /// Returns an iterator over the values in the collection.
430 ///
431 /// # Examples
432 ///
433 /// ```rust
434 /// use scim_server::resource::value_objects::{MultiValuedAttribute, EmailAddress};
435 ///
436 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
437 /// let emails = vec![
438 /// EmailAddress::new_simple("work@example.com".to_string())?,
439 /// EmailAddress::new_simple("personal@example.com".to_string())?,
440 /// ];
441 /// let multi_attr = MultiValuedAttribute::new(emails)?;
442 /// for value in multi_attr.iter() {
443 /// println!("Value: {:?}", value);
444 /// }
445 /// Ok(())
446 /// }
447 /// ```
448 pub fn iter(&self) -> std::slice::Iter<'_, T> {
449 self.values.iter()
450 }
451
452 /// Validates that at most one value is marked as primary.
453 ///
454 /// This method is primarily used internally to ensure invariants
455 /// are maintained during construction and modification.
456 ///
457 /// # Returns
458 ///
459 /// * `Ok(())` - Validation passed
460 /// * `Err(ValidationError)` - Multiple primary values detected
461 pub fn validate_single_primary(&self) -> ValidationResult<()> {
462 if let Some(index) = self.primary_index {
463 if index >= self.values.len() {
464 return Err(ValidationError::custom(
465 "Primary index points to non-existent value",
466 ));
467 }
468 }
469 Ok(())
470 }
471
472 /// Finds the first value that matches the given predicate.
473 ///
474 /// # Arguments
475 ///
476 /// * `predicate` - Function to test each value
477 ///
478 /// # Returns
479 ///
480 /// * `Some(&T)` - Reference to the first matching value
481 /// * `None` - No value matches the predicate
482 ///
483 /// # Examples
484 ///
485 /// ```rust
486 /// use scim_server::resource::value_objects::{MultiValuedAttribute, EmailAddress};
487 ///
488 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
489 /// let emails = vec![
490 /// EmailAddress::new_simple("work@example.com".to_string())?,
491 /// EmailAddress::new_simple("personal@example.com".to_string())?,
492 /// ];
493 /// let multi_attr = MultiValuedAttribute::new(emails)?;
494 /// let found = multi_attr.find(|value| value.value().contains("work"));
495 /// assert!(found.is_some());
496 /// Ok(())
497 /// }
498 /// ```
499 pub fn find<P>(&self, predicate: P) -> Option<&T>
500 where
501 P: Fn(&T) -> bool,
502 {
503 self.values.iter().find(|&value| predicate(value))
504 }
505
506 /// Returns all values that satisfy the given predicate.
507 /// Filters values that match the given predicate.
508 ///
509 /// # Arguments
510 ///
511 /// * `predicate` - Function to test each value
512 ///
513 /// # Returns
514 ///
515 /// An iterator over references to matching values
516 ///
517 /// # Examples
518 ///
519 /// ```rust
520 /// use scim_server::resource::value_objects::{MultiValuedAttribute, EmailAddress};
521 ///
522 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
523 /// let emails = vec![
524 /// EmailAddress::new_simple("work@example.com".to_string())?,
525 /// EmailAddress::new_simple("personal@example.com".to_string())?,
526 /// ];
527 /// let multi_attr = MultiValuedAttribute::new(emails)?;
528 /// let work_values = multi_attr.filter(|v| v.value().contains("work"));
529 /// assert_eq!(work_values.len(), 1);
530 /// Ok(())
531 /// }
532 /// ```
533 pub fn filter<P>(&self, predicate: P) -> Vec<&T>
534 where
535 P: Fn(&T) -> bool,
536 {
537 self.values
538 .iter()
539 .filter(|&value| predicate(value))
540 .collect()
541 }
542
543 /// Converts the multi-valued attribute into a vector of values.
544 ///
545 /// This consumes the multi-valued attribute and returns the underlying vector.
546 ///
547 /// # Examples
548 ///
549 /// ```rust
550 /// use scim_server::resource::value_objects::{MultiValuedAttribute, EmailAddress};
551 ///
552 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
553 /// let emails = vec![
554 /// EmailAddress::new_simple("work@example.com".to_string())?,
555 /// EmailAddress::new_simple("personal@example.com".to_string())?,
556 /// ];
557 /// let multi_attr = MultiValuedAttribute::new(emails)?;
558 /// let values_vec = multi_attr.into_values();
559 /// assert_eq!(values_vec.len(), 2);
560 /// Ok(())
561 /// }
562 /// ```
563 pub fn into_values(self) -> Vec<T> {
564 self.values
565 }
566}
567
568impl<T> Default for MultiValuedAttribute<T> {
569 /// Creates an empty multi-valued attribute.
570 ///
571 /// Note: This creates an empty collection which may not be valid
572 /// for all use cases. Use `new()` for validated construction.
573 fn default() -> Self {
574 Self::empty()
575 }
576}
577
578impl<T> From<Vec<T>> for MultiValuedAttribute<T> {
579 /// Creates a multi-valued attribute from a vector of values.
580 ///
581 /// This bypasses validation and should be used carefully.
582 /// Consider using `new()` for validated construction.
583 fn from(values: Vec<T>) -> Self {
584 Self {
585 values,
586 primary_index: None,
587 }
588 }
589}
590
591impl<T> From<T> for MultiValuedAttribute<T> {
592 /// Creates a multi-valued attribute from a single value.
593 fn from(value: T) -> Self {
594 Self::single(value)
595 }
596}
597
598impl<T> IntoIterator for MultiValuedAttribute<T> {
599 type Item = T;
600 type IntoIter = std::vec::IntoIter<T>;
601
602 /// Creates an iterator that yields owned values.
603 fn into_iter(self) -> Self::IntoIter {
604 self.values.into_iter()
605 }
606}
607
608impl<'a, T> IntoIterator for &'a MultiValuedAttribute<T> {
609 type Item = &'a T;
610 type IntoIter = std::slice::Iter<'a, T>;
611
612 /// Creates an iterator that yields borrowed values.
613 fn into_iter(self) -> Self::IntoIter {
614 self.iter()
615 }
616}
617
618impl<T: fmt::Display> fmt::Display for MultiValuedAttribute<T> {
619 /// Formats the multi-valued attribute for display.
620 ///
621 /// Shows the number of values and indicates which one is primary.
622 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
623 match self.primary() {
624 Some(primary) => write!(
625 f,
626 "MultiValuedAttribute({} values, primary: {})",
627 self.len(),
628 primary
629 ),
630 None => write!(f, "MultiValuedAttribute({} values)", self.len()),
631 }
632 }
633}
634
635#[cfg(test)]
636mod tests {
637 use super::*;
638
639 #[derive(Debug, Clone, PartialEq)]
640 struct TestValue {
641 id: String,
642 value_type: String,
643 }
644
645 impl fmt::Display for TestValue {
646 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
647 write!(f, "{}: {}", self.value_type, self.id)
648 }
649 }
650
651 fn create_test_values() -> Vec<TestValue> {
652 vec![
653 TestValue {
654 id: "1".to_string(),
655 value_type: "work".to_string(),
656 },
657 TestValue {
658 id: "2".to_string(),
659 value_type: "home".to_string(),
660 },
661 TestValue {
662 id: "3".to_string(),
663 value_type: "other".to_string(),
664 },
665 ]
666 }
667
668 #[test]
669 fn test_new_valid() {
670 let values = create_test_values();
671 let multi_attr = MultiValuedAttribute::new(values.clone()).unwrap();
672
673 assert_eq!(multi_attr.len(), 3);
674 assert!(!multi_attr.is_empty());
675 assert!(multi_attr.primary().is_none());
676 assert_eq!(multi_attr.values(), &values);
677 }
678
679 #[test]
680 fn test_new_empty_fails() {
681 let result = MultiValuedAttribute::<TestValue>::new(vec![]);
682 assert!(result.is_err());
683 assert!(result.unwrap_err().to_string().contains("cannot be empty"));
684 }
685
686 #[test]
687 fn test_single() {
688 let value = TestValue {
689 id: "1".to_string(),
690 value_type: "work".to_string(),
691 };
692 let multi_attr = MultiValuedAttribute::single(value.clone());
693
694 assert_eq!(multi_attr.len(), 1);
695 assert!(multi_attr.primary().is_none());
696 assert_eq!(multi_attr.get(0), Some(&value));
697 }
698
699 #[test]
700 fn test_single_primary() {
701 let value = TestValue {
702 id: "1".to_string(),
703 value_type: "work".to_string(),
704 };
705 let multi_attr = MultiValuedAttribute::single_primary(value.clone());
706
707 assert_eq!(multi_attr.len(), 1);
708 assert_eq!(multi_attr.primary(), Some(&value));
709 assert_eq!(multi_attr.primary_index(), Some(0));
710 }
711
712 #[test]
713 fn test_with_primary() {
714 let values = create_test_values();
715 let multi_attr = MultiValuedAttribute::new(values.clone())
716 .unwrap()
717 .with_primary(1)
718 .unwrap();
719
720 assert_eq!(multi_attr.primary(), Some(&values[1]));
721 assert_eq!(multi_attr.primary_index(), Some(1));
722 }
723
724 #[test]
725 fn test_with_primary_invalid_index() {
726 let values = create_test_values();
727 let result = MultiValuedAttribute::new(values).unwrap().with_primary(10);
728
729 assert!(result.is_err());
730 assert!(result.unwrap_err().to_string().contains("out of bounds"));
731 }
732
733 #[test]
734 fn test_without_primary() {
735 let values = create_test_values();
736 let multi_attr = MultiValuedAttribute::new(values)
737 .unwrap()
738 .with_primary(0)
739 .unwrap()
740 .without_primary();
741
742 assert!(multi_attr.primary().is_none());
743 assert_eq!(multi_attr.primary_index(), None);
744 }
745
746 #[test]
747 fn test_with_value() {
748 let values = create_test_values();
749 let new_value = TestValue {
750 id: "4".to_string(),
751 value_type: "mobile".to_string(),
752 };
753
754 let multi_attr = MultiValuedAttribute::new(values)
755 .unwrap()
756 .with_value(new_value.clone());
757
758 assert_eq!(multi_attr.len(), 4);
759 assert_eq!(multi_attr.get(3), Some(&new_value));
760 }
761
762 #[test]
763 fn test_with_primary_value() {
764 let values = create_test_values();
765 let new_value = TestValue {
766 id: "4".to_string(),
767 value_type: "mobile".to_string(),
768 };
769
770 let multi_attr = MultiValuedAttribute::new(values)
771 .unwrap()
772 .with_primary_value(new_value.clone());
773
774 assert_eq!(multi_attr.len(), 4);
775 assert_eq!(multi_attr.primary(), Some(&new_value));
776 assert_eq!(multi_attr.primary_index(), Some(3));
777 }
778
779 #[test]
780 fn test_find() {
781 let values = create_test_values();
782 let multi_attr = MultiValuedAttribute::new(values.clone()).unwrap();
783
784 let work_value = multi_attr.find(|v| v.value_type == "work");
785 assert_eq!(work_value, Some(&values[0]));
786
787 let mobile_value = multi_attr.find(|v| v.value_type == "mobile");
788 assert_eq!(mobile_value, None);
789 }
790
791 #[test]
792 fn test_filter() {
793 let values = create_test_values();
794 let multi_attr = MultiValuedAttribute::new(values.clone()).unwrap();
795
796 let work_values = multi_attr.filter(|v| v.value_type == "work");
797 assert_eq!(work_values, vec![&values[0]]);
798
799 let non_work_values = multi_attr.filter(|v| v.value_type != "work");
800 assert_eq!(non_work_values, vec![&values[1], &values[2]]);
801 }
802
803 #[test]
804 fn test_into_values() {
805 let values = create_test_values();
806 let expected = values.clone();
807 let multi_attr = MultiValuedAttribute::new(values).unwrap();
808
809 let extracted_values = multi_attr.into_values();
810 assert_eq!(extracted_values, expected);
811 }
812
813 #[test]
814 fn test_iter() {
815 let values = create_test_values();
816 let multi_attr = MultiValuedAttribute::new(values.clone()).unwrap();
817
818 let collected: Vec<&TestValue> = multi_attr.iter().collect();
819 let expected: Vec<&TestValue> = values.iter().collect();
820 assert_eq!(collected, expected);
821 }
822
823 #[test]
824 fn test_into_iter_owned() {
825 let values = create_test_values();
826 let expected = values.clone();
827 let multi_attr = MultiValuedAttribute::new(values).unwrap();
828
829 let collected: Vec<TestValue> = multi_attr.into_iter().collect();
830 assert_eq!(collected, expected);
831 }
832
833 #[test]
834 fn test_into_iter_borrowed() {
835 let values = create_test_values();
836 let multi_attr = MultiValuedAttribute::new(values.clone()).unwrap();
837
838 let collected: Vec<&TestValue> = (&multi_attr).into_iter().collect();
839 let expected: Vec<&TestValue> = values.iter().collect();
840 assert_eq!(collected, expected);
841 }
842
843 #[test]
844 fn test_validate_single_primary() {
845 let values = create_test_values();
846 let multi_attr = MultiValuedAttribute::new(values)
847 .unwrap()
848 .with_primary(1)
849 .unwrap();
850
851 assert!(multi_attr.validate_single_primary().is_ok());
852 }
853
854 #[test]
855 fn test_validate_single_primary_invalid_index() {
856 let values = create_test_values();
857 let mut multi_attr = MultiValuedAttribute::new(values).unwrap();
858 // Manually set invalid primary index for testing
859 multi_attr.primary_index = Some(10);
860
861 assert!(multi_attr.validate_single_primary().is_err());
862 }
863
864 #[test]
865 fn test_default() {
866 let multi_attr = MultiValuedAttribute::<TestValue>::default();
867 assert!(multi_attr.is_empty());
868 assert_eq!(multi_attr.len(), 0);
869 }
870
871 #[test]
872 fn test_from_vec() {
873 let values = create_test_values();
874 let multi_attr: MultiValuedAttribute<TestValue> =
875 MultiValuedAttribute::from(values.clone());
876
877 assert_eq!(multi_attr.len(), 3);
878 assert_eq!(multi_attr.values(), &values);
879 }
880
881 #[test]
882 fn test_from_single_value() {
883 let value = TestValue {
884 id: "1".to_string(),
885 value_type: "work".to_string(),
886 };
887 let multi_attr = MultiValuedAttribute::from(value.clone());
888
889 assert_eq!(multi_attr.len(), 1);
890 assert_eq!(multi_attr.get(0), Some(&value));
891 }
892
893 #[test]
894 fn test_display() {
895 let values = create_test_values();
896 let multi_attr = MultiValuedAttribute::new(values).unwrap();
897
898 let display_str = format!("{}", multi_attr);
899 assert!(display_str.contains("MultiValuedAttribute(3 values)"));
900
901 let with_primary = multi_attr.with_primary(0).unwrap();
902 let primary_display = format!("{}", with_primary);
903 assert!(primary_display.contains("primary: work: 1"));
904 }
905
906 #[test]
907 fn test_get_valid_index() {
908 let values = create_test_values();
909 let multi_attr = MultiValuedAttribute::new(values.clone()).unwrap();
910
911 assert_eq!(multi_attr.get(0), Some(&values[0]));
912 assert_eq!(multi_attr.get(1), Some(&values[1]));
913 assert_eq!(multi_attr.get(2), Some(&values[2]));
914 }
915
916 #[test]
917 fn test_get_invalid_index() {
918 let values = create_test_values();
919 let multi_attr = MultiValuedAttribute::new(values).unwrap();
920
921 assert_eq!(multi_attr.get(10), None);
922 }
923
924 #[test]
925 fn test_empty() {
926 let multi_attr = MultiValuedAttribute::<TestValue>::empty();
927 assert!(multi_attr.is_empty());
928 assert_eq!(multi_attr.len(), 0);
929 assert!(multi_attr.primary().is_none());
930 }
931}