use crate::error::{ValidationError, ValidationResult};
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MultiValuedAttribute<T> {
values: Vec<T>,
primary_index: Option<usize>,
}
impl<T> MultiValuedAttribute<T> {
pub fn new(values: Vec<T>) -> ValidationResult<Self> {
if values.is_empty() {
return Err(ValidationError::custom(
"Multi-valued attribute cannot be empty",
));
}
Ok(Self {
values,
primary_index: None,
})
}
pub fn single(value: T) -> Self {
Self {
values: vec![value],
primary_index: None,
}
}
pub fn single_primary(value: T) -> Self {
Self {
values: vec![value],
primary_index: Some(0),
}
}
pub(crate) fn empty() -> Self {
Self {
values: Vec::new(),
primary_index: None,
}
}
pub fn len(&self) -> usize {
self.values.len()
}
pub fn is_empty(&self) -> bool {
self.values.is_empty()
}
pub fn primary(&self) -> Option<&T> {
self.primary_index.and_then(|index| self.values.get(index))
}
pub fn primary_index(&self) -> Option<usize> {
self.primary_index
}
pub fn values(&self) -> &[T] {
&self.values
}
pub fn get(&self, index: usize) -> Option<&T> {
self.values.get(index)
}
pub fn with_primary(mut self, index: usize) -> ValidationResult<Self> {
if index >= self.values.len() {
return Err(ValidationError::custom(format!(
"Primary index {} is out of bounds for collection of size {}",
index,
self.values.len()
)));
}
self.primary_index = Some(index);
Ok(self)
}
pub fn without_primary(mut self) -> Self {
self.primary_index = None;
self
}
pub fn with_value(mut self, value: T) -> Self {
self.values.push(value);
self
}
pub fn with_primary_value(mut self, value: T) -> Self {
self.values.push(value);
self.primary_index = Some(self.values.len() - 1);
self
}
pub fn iter(&self) -> std::slice::Iter<'_, T> {
self.values.iter()
}
pub fn validate_single_primary(&self) -> ValidationResult<()> {
if let Some(index) = self.primary_index {
if index >= self.values.len() {
return Err(ValidationError::custom(
"Primary index points to non-existent value",
));
}
}
Ok(())
}
pub fn find<P>(&self, predicate: P) -> Option<&T>
where
P: Fn(&T) -> bool,
{
self.values.iter().find(|&value| predicate(value))
}
pub fn filter<P>(&self, predicate: P) -> Vec<&T>
where
P: Fn(&T) -> bool,
{
self.values
.iter()
.filter(|&value| predicate(value))
.collect()
}
pub fn into_values(self) -> Vec<T> {
self.values
}
}
impl<T> Default for MultiValuedAttribute<T> {
fn default() -> Self {
Self::empty()
}
}
impl<T> From<Vec<T>> for MultiValuedAttribute<T> {
fn from(values: Vec<T>) -> Self {
Self {
values,
primary_index: None,
}
}
}
impl<T> From<T> for MultiValuedAttribute<T> {
fn from(value: T) -> Self {
Self::single(value)
}
}
impl<T> IntoIterator for MultiValuedAttribute<T> {
type Item = T;
type IntoIter = std::vec::IntoIter<T>;
fn into_iter(self) -> Self::IntoIter {
self.values.into_iter()
}
}
impl<'a, T> IntoIterator for &'a MultiValuedAttribute<T> {
type Item = &'a T;
type IntoIter = std::slice::Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<T: fmt::Display> fmt::Display for MultiValuedAttribute<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.primary() {
Some(primary) => write!(
f,
"MultiValuedAttribute({} values, primary: {})",
self.len(),
primary
),
None => write!(f, "MultiValuedAttribute({} values)", self.len()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, Clone, PartialEq)]
struct TestValue {
id: String,
value_type: String,
}
impl fmt::Display for TestValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}: {}", self.value_type, self.id)
}
}
fn create_test_values() -> Vec<TestValue> {
vec![
TestValue {
id: "1".to_string(),
value_type: "work".to_string(),
},
TestValue {
id: "2".to_string(),
value_type: "home".to_string(),
},
TestValue {
id: "3".to_string(),
value_type: "other".to_string(),
},
]
}
#[test]
fn test_new_valid() {
let values = create_test_values();
let multi_attr = MultiValuedAttribute::new(values.clone()).unwrap();
assert_eq!(multi_attr.len(), 3);
assert!(!multi_attr.is_empty());
assert!(multi_attr.primary().is_none());
assert_eq!(multi_attr.values(), &values);
}
#[test]
fn test_new_empty_fails() {
let result = MultiValuedAttribute::<TestValue>::new(vec![]);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("cannot be empty"));
}
#[test]
fn test_single() {
let value = TestValue {
id: "1".to_string(),
value_type: "work".to_string(),
};
let multi_attr = MultiValuedAttribute::single(value.clone());
assert_eq!(multi_attr.len(), 1);
assert!(multi_attr.primary().is_none());
assert_eq!(multi_attr.get(0), Some(&value));
}
#[test]
fn test_single_primary() {
let value = TestValue {
id: "1".to_string(),
value_type: "work".to_string(),
};
let multi_attr = MultiValuedAttribute::single_primary(value.clone());
assert_eq!(multi_attr.len(), 1);
assert_eq!(multi_attr.primary(), Some(&value));
assert_eq!(multi_attr.primary_index(), Some(0));
}
#[test]
fn test_with_primary() {
let values = create_test_values();
let multi_attr = MultiValuedAttribute::new(values.clone())
.unwrap()
.with_primary(1)
.unwrap();
assert_eq!(multi_attr.primary(), Some(&values[1]));
assert_eq!(multi_attr.primary_index(), Some(1));
}
#[test]
fn test_with_primary_invalid_index() {
let values = create_test_values();
let result = MultiValuedAttribute::new(values).unwrap().with_primary(10);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("out of bounds"));
}
#[test]
fn test_without_primary() {
let values = create_test_values();
let multi_attr = MultiValuedAttribute::new(values)
.unwrap()
.with_primary(0)
.unwrap()
.without_primary();
assert!(multi_attr.primary().is_none());
assert_eq!(multi_attr.primary_index(), None);
}
#[test]
fn test_with_value() {
let values = create_test_values();
let new_value = TestValue {
id: "4".to_string(),
value_type: "mobile".to_string(),
};
let multi_attr = MultiValuedAttribute::new(values)
.unwrap()
.with_value(new_value.clone());
assert_eq!(multi_attr.len(), 4);
assert_eq!(multi_attr.get(3), Some(&new_value));
}
#[test]
fn test_with_primary_value() {
let values = create_test_values();
let new_value = TestValue {
id: "4".to_string(),
value_type: "mobile".to_string(),
};
let multi_attr = MultiValuedAttribute::new(values)
.unwrap()
.with_primary_value(new_value.clone());
assert_eq!(multi_attr.len(), 4);
assert_eq!(multi_attr.primary(), Some(&new_value));
assert_eq!(multi_attr.primary_index(), Some(3));
}
#[test]
fn test_find() {
let values = create_test_values();
let multi_attr = MultiValuedAttribute::new(values.clone()).unwrap();
let work_value = multi_attr.find(|v| v.value_type == "work");
assert_eq!(work_value, Some(&values[0]));
let mobile_value = multi_attr.find(|v| v.value_type == "mobile");
assert_eq!(mobile_value, None);
}
#[test]
fn test_filter() {
let values = create_test_values();
let multi_attr = MultiValuedAttribute::new(values.clone()).unwrap();
let work_values = multi_attr.filter(|v| v.value_type == "work");
assert_eq!(work_values, vec![&values[0]]);
let non_work_values = multi_attr.filter(|v| v.value_type != "work");
assert_eq!(non_work_values, vec![&values[1], &values[2]]);
}
#[test]
fn test_into_values() {
let values = create_test_values();
let expected = values.clone();
let multi_attr = MultiValuedAttribute::new(values).unwrap();
let extracted_values = multi_attr.into_values();
assert_eq!(extracted_values, expected);
}
#[test]
fn test_iter() {
let values = create_test_values();
let multi_attr = MultiValuedAttribute::new(values.clone()).unwrap();
let collected: Vec<&TestValue> = multi_attr.iter().collect();
let expected: Vec<&TestValue> = values.iter().collect();
assert_eq!(collected, expected);
}
#[test]
fn test_into_iter_owned() {
let values = create_test_values();
let expected = values.clone();
let multi_attr = MultiValuedAttribute::new(values).unwrap();
let collected: Vec<TestValue> = multi_attr.into_iter().collect();
assert_eq!(collected, expected);
}
#[test]
fn test_into_iter_borrowed() {
let values = create_test_values();
let multi_attr = MultiValuedAttribute::new(values.clone()).unwrap();
let collected: Vec<&TestValue> = (&multi_attr).into_iter().collect();
let expected: Vec<&TestValue> = values.iter().collect();
assert_eq!(collected, expected);
}
#[test]
fn test_validate_single_primary() {
let values = create_test_values();
let multi_attr = MultiValuedAttribute::new(values)
.unwrap()
.with_primary(1)
.unwrap();
assert!(multi_attr.validate_single_primary().is_ok());
}
#[test]
fn test_validate_single_primary_invalid_index() {
let values = create_test_values();
let mut multi_attr = MultiValuedAttribute::new(values).unwrap();
multi_attr.primary_index = Some(10);
assert!(multi_attr.validate_single_primary().is_err());
}
#[test]
fn test_default() {
let multi_attr = MultiValuedAttribute::<TestValue>::default();
assert!(multi_attr.is_empty());
assert_eq!(multi_attr.len(), 0);
}
#[test]
fn test_from_vec() {
let values = create_test_values();
let multi_attr: MultiValuedAttribute<TestValue> =
MultiValuedAttribute::from(values.clone());
assert_eq!(multi_attr.len(), 3);
assert_eq!(multi_attr.values(), &values);
}
#[test]
fn test_from_single_value() {
let value = TestValue {
id: "1".to_string(),
value_type: "work".to_string(),
};
let multi_attr = MultiValuedAttribute::from(value.clone());
assert_eq!(multi_attr.len(), 1);
assert_eq!(multi_attr.get(0), Some(&value));
}
#[test]
fn test_display() {
let values = create_test_values();
let multi_attr = MultiValuedAttribute::new(values).unwrap();
let display_str = format!("{}", multi_attr);
assert!(display_str.contains("MultiValuedAttribute(3 values)"));
let with_primary = multi_attr.with_primary(0).unwrap();
let primary_display = format!("{}", with_primary);
assert!(primary_display.contains("primary: work: 1"));
}
#[test]
fn test_get_valid_index() {
let values = create_test_values();
let multi_attr = MultiValuedAttribute::new(values.clone()).unwrap();
assert_eq!(multi_attr.get(0), Some(&values[0]));
assert_eq!(multi_attr.get(1), Some(&values[1]));
assert_eq!(multi_attr.get(2), Some(&values[2]));
}
#[test]
fn test_get_invalid_index() {
let values = create_test_values();
let multi_attr = MultiValuedAttribute::new(values).unwrap();
assert_eq!(multi_attr.get(10), None);
}
#[test]
fn test_empty() {
let multi_attr = MultiValuedAttribute::<TestValue>::empty();
assert!(multi_attr.is_empty());
assert_eq!(multi_attr.len(), 0);
assert!(multi_attr.primary().is_none());
}
}