use std::fmt;
use std::hash::{Hash, Hasher};
use derive_new::new;
use crate::Part;
use crate::errors::ErnError;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(new, Debug, PartialEq, Clone, Eq, Default, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Parts(pub(crate) Vec<Part>);
impl Parts {
pub fn add_part<T>(mut self, part: T) -> Result<Self, ErnError>
where
T: Into<Part>,
{
if self.0.len() >= 10 {
return Err(ErnError::ParseFailure(
"Parts",
"cannot exceed maximum of 10 parts".to_string(),
));
}
self.0.push(part.into());
Ok(self)
}
pub fn into_owned(self) -> Parts {
Parts(self.0.into_iter().collect())
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
impl Hash for Parts {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.len().hash(state);
for part in &self.0 {
part.hash(state);
}
}
}
impl FromIterator<Part> for Parts {
fn from_iter<T: IntoIterator<Item = Part>>(iter: T) -> Self {
Parts(iter.into_iter().collect())
}
}
impl fmt::Display for Parts {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
self.0
.iter()
.map(|p| p.as_str())
.collect::<Vec<_>>()
.join("/")
)
}
}
impl IntoIterator for Parts {
type Item = Part;
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<'a> IntoIterator for &'a Parts {
type Item = &'a Part;
type IntoIter = std::slice::Iter<'a, Part>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parts_creation() -> anyhow::Result<()> {
let parts = Parts::new(vec![Part::new("segment1")?, Part::new("segment2")?]);
assert_eq!(parts.to_string(), "segment1/segment2");
Ok(())
}
#[test]
fn test_parts_add_part() -> anyhow::Result<()> {
let mut parts = Parts::new(vec![Part::new("segment1")?]);
parts = parts.add_part(Part::new("segment2")?)?;
parts = parts.add_part(Part::new("segment3")?)?;
assert_eq!(parts.to_string(), "segment1/segment2/segment3");
Ok(())
}
#[test]
fn test_parts_from_iterator() -> anyhow::Result<()> {
let parts: Result<Parts, _> = vec!["segment1", "segment2", "segment3"]
.into_iter()
.map(Part::new)
.collect();
match parts {
Ok(parts) => {
assert_eq!(parts.to_string(), "segment1/segment2/segment3");
Ok(())
}
Err(e) => Err(anyhow::anyhow!(e)),
}
}
#[test]
fn test_parts_into_owned() -> anyhow::Result<()> {
let parts = Parts::new(vec![Part::new("segment1")?, Part::new("segment2")?]);
let owned_parts: Parts = parts;
assert_eq!(owned_parts.to_string(), "segment1/segment2");
Ok(())
}
#[test]
fn test_parts_iterator() -> anyhow::Result<()> {
let parts = Parts::new(vec![Part::new("segment1")?, Part::new("segment2")?]);
let collected: Vec<_> = parts.into_iter().collect();
assert_eq!(collected.len(), 2);
assert_eq!(collected[0].as_str(), "segment1");
assert_eq!(collected[1].as_str(), "segment2");
Ok(())
}
#[test]
fn test_parts_ref_iterator() -> anyhow::Result<()> {
let parts = Parts::new(vec![Part::new("segment1")?, Part::new("segment2")?]);
let collected: Vec<_> = (&parts).into_iter().map(|p| p.as_str()).collect();
assert_eq!(collected, vec!["segment1", "segment2"]);
Ok(())
}
#[test]
fn test_parts_for_loop() -> anyhow::Result<()> {
let parts = Parts::new(vec![Part::new("segment1")?, Part::new("segment2")?]);
let mut collected = Vec::new();
for part in parts {
collected.push(part.as_str().to_string());
}
assert_eq!(
collected,
vec!["segment1".to_string(), "segment2".to_string()]
);
Ok(())
}
#[test]
fn test_parts_validation_max_parts() -> anyhow::Result<()> {
let mut parts = Parts::new(vec![]);
for i in 0..10 {
parts = parts.add_part(Part::new(format!("part{}", i))?)?;
}
let result = parts.add_part(Part::new("one_too_many")?);
assert!(result.is_err());
match result {
Err(ErnError::ParseFailure(component, msg)) => {
assert_eq!(component, "Parts");
assert!(msg.contains("cannot exceed maximum"));
}
_ => panic!("Expected ParseFailure error for too many parts"),
}
Ok(())
}
}