acton_ern/model/
parts.rs

1use std::fmt;
2use std::hash::{Hash, Hasher};
3
4use derive_new::new;
5
6use crate::Part;
7
8/// Represents a collection of parts in the ERN (Entity Resource Name), handling multiple segments.
9#[derive(new, Debug, PartialEq, Clone, Eq, Default, PartialOrd)]
10pub struct Parts(pub(crate) Vec<Part>);
11
12impl Parts {
13    /// Adds a part to the collection.
14    ///
15    /// # Arguments
16    ///
17    /// * `part` - The `Part` to be added to the collection.
18    pub fn add_part<T>(mut self, part: T) -> Self
19    where
20        T: Into<Part>,
21    {
22        self.0.push(part.into());
23        self
24    }
25
26    /// Converts the Parts into an owned version with 'static lifetime
27    pub fn into_owned(self) -> Parts {
28        Parts(self.0.into_iter().map(|part| part).collect())
29    }
30
31    /// Returns the number of parts in the collection.
32    pub fn len(&self) -> usize {
33        self.0.len()
34    }
35
36    /// Returns true if the collection is empty.
37    pub fn is_empty(&self) -> bool {
38        self.0.is_empty()
39    }
40}
41
42impl Hash for Parts {
43    fn hash<H: Hasher>(&self, state: &mut H) {
44        self.0.len().hash(state);
45        for part in &self.0 {
46            part.hash(state);
47        }
48    }
49}
50
51impl FromIterator<Part> for Parts {
52    fn from_iter<T: IntoIterator<Item=Part>>(iter: T) -> Self {
53        Parts(iter.into_iter().collect())
54    }
55}
56
57impl fmt::Display for Parts {
58    /// Formats the collection of parts as a string, joining them with '/'.
59    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60        write!(f, "{}", self.0.iter().map(|p| p.as_str()).collect::<Vec<_>>().join("/"))
61    }
62}
63
64impl IntoIterator for Parts {
65    type Item = Part;
66    type IntoIter = std::vec::IntoIter<Self::Item>;
67
68    fn into_iter(self) -> Self::IntoIter {
69        self.0.into_iter()
70    }
71}
72
73impl<'a> IntoIterator for &'a Parts {
74    type Item = &'a Part;
75    type IntoIter = std::slice::Iter<'a, Part>;
76
77    fn into_iter(self) -> Self::IntoIter {
78        self.0.iter()
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85
86    #[test]
87    fn test_parts_creation() -> anyhow::Result<()> {
88        let parts = Parts::new(vec![Part::new("segment1")?, Part::new("segment2")?]);
89        assert_eq!(parts.to_string(), "segment1/segment2");
90        Ok(())
91    }
92
93    #[test]
94    fn test_parts_add_part() -> anyhow::Result<()> {
95        let parts = Parts::new(vec![Part::new("segment1")?])
96            .add_part(Part::new("segment2")?)
97            .add_part(Part::new("segment3")?);
98        assert_eq!(parts.to_string(), "segment1/segment2/segment3");
99        Ok(())
100    }
101
102    #[test]
103    fn test_parts_from_iterator() -> anyhow::Result<()> {
104        let parts: Result<Parts, _> = vec!["segment1", "segment2", "segment3"]
105            .into_iter()
106            .map(Part::new)
107            .collect();
108        match parts {
109            Ok(parts) => {
110                assert_eq!(parts.to_string(), "segment1/segment2/segment3");
111                Ok(())
112            }
113            Err(e) => Err(anyhow::anyhow!(e)),
114        }
115    }
116
117    #[test]
118    fn test_parts_into_owned() -> anyhow::Result<()> {
119        let parts = Parts::new(vec![Part::new("segment1")?, Part::new("segment2")?]);
120        let owned_parts: Parts = parts;
121        assert_eq!(owned_parts.to_string(), "segment1/segment2");
122        Ok(())
123    }
124
125    #[test]
126    fn test_parts_iterator() -> anyhow::Result<()> {
127        let parts = Parts::new(vec![Part::new("segment1")?, Part::new("segment2")?]);
128        let collected: Vec<_> = parts.into_iter().collect();
129        assert_eq!(collected.len(), 2);
130        assert_eq!(collected[0].as_str(), "segment1");
131        assert_eq!(collected[1].as_str(), "segment2");
132        Ok(())
133    }
134
135    #[test]
136    fn test_parts_ref_iterator() -> anyhow::Result<()> {
137        let parts = Parts::new(vec![Part::new("segment1")?, Part::new("segment2")?]);
138        let collected: Vec<_> = (&parts).into_iter().map(|p| p.as_str()).collect();
139        assert_eq!(collected, vec!["segment1", "segment2"]);
140        Ok(())
141    }
142
143    #[test]
144    fn test_parts_for_loop() -> anyhow::Result<()> {
145        let parts = Parts::new(vec![Part::new("segment1")?, Part::new("segment2")?]);
146        let mut collected = Vec::new();
147        for part in parts {
148            collected.push(part.as_str().to_string());
149        }
150        assert_eq!(collected, vec!["segment1".to_string(), "segment2".to_string()]);
151        Ok(())
152    }
153}