1use std::fmt;
2use std::hash::{Hash, Hasher};
3
4use derive_new::new;
5
6use crate::Part;
7use crate::errors::ErnError;
8
9#[cfg(feature = "serde")]
10use serde::{Deserialize, Serialize};
11
12#[derive(new, Debug, PartialEq, Clone, Eq, Default, PartialOrd)]
14#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
15pub struct Parts(pub(crate) Vec<Part>);
16
17impl Parts {
18 pub fn add_part<T>(mut self, part: T) -> Result<Self, ErnError>
37 where
38 T: Into<Part>,
39 {
40 if self.0.len() >= 10 {
42 return Err(ErnError::ParseFailure(
43 "Parts",
44 "cannot exceed maximum of 10 parts".to_string(),
45 ));
46 }
47
48 self.0.push(part.into());
49 Ok(self)
50 }
51
52 pub fn into_owned(self) -> Parts {
54 Parts(self.0.into_iter().collect())
55 }
56
57 pub fn len(&self) -> usize {
59 self.0.len()
60 }
61
62 pub fn is_empty(&self) -> bool {
64 self.0.is_empty()
65 }
66}
67
68impl Hash for Parts {
69 fn hash<H: Hasher>(&self, state: &mut H) {
70 self.0.len().hash(state);
71 for part in &self.0 {
72 part.hash(state);
73 }
74 }
75}
76
77impl FromIterator<Part> for Parts {
78 fn from_iter<T: IntoIterator<Item = Part>>(iter: T) -> Self {
79 Parts(iter.into_iter().collect())
80 }
81}
82
83impl fmt::Display for Parts {
84 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86 write!(
87 f,
88 "{}",
89 self.0
90 .iter()
91 .map(|p| p.as_str())
92 .collect::<Vec<_>>()
93 .join("/")
94 )
95 }
96}
97
98impl IntoIterator for Parts {
99 type Item = Part;
100 type IntoIter = std::vec::IntoIter<Self::Item>;
101
102 fn into_iter(self) -> Self::IntoIter {
103 self.0.into_iter()
104 }
105}
106
107impl<'a> IntoIterator for &'a Parts {
108 type Item = &'a Part;
109 type IntoIter = std::slice::Iter<'a, Part>;
110
111 fn into_iter(self) -> Self::IntoIter {
112 self.0.iter()
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119
120 #[test]
121 fn test_parts_creation() -> anyhow::Result<()> {
122 let parts = Parts::new(vec![Part::new("segment1")?, Part::new("segment2")?]);
123 assert_eq!(parts.to_string(), "segment1/segment2");
124 Ok(())
125 }
126
127 #[test]
128 fn test_parts_add_part() -> anyhow::Result<()> {
129 let mut parts = Parts::new(vec![Part::new("segment1")?]);
130 parts = parts.add_part(Part::new("segment2")?)?;
131 parts = parts.add_part(Part::new("segment3")?)?;
132
133 assert_eq!(parts.to_string(), "segment1/segment2/segment3");
134 Ok(())
135 }
136
137 #[test]
138 fn test_parts_from_iterator() -> anyhow::Result<()> {
139 let parts: Result<Parts, _> = vec!["segment1", "segment2", "segment3"]
140 .into_iter()
141 .map(Part::new)
142 .collect();
143 match parts {
144 Ok(parts) => {
145 assert_eq!(parts.to_string(), "segment1/segment2/segment3");
146 Ok(())
147 }
148 Err(e) => Err(anyhow::anyhow!(e)),
149 }
150 }
151
152 #[test]
153 fn test_parts_into_owned() -> anyhow::Result<()> {
154 let parts = Parts::new(vec![Part::new("segment1")?, Part::new("segment2")?]);
155 let owned_parts: Parts = parts;
156 assert_eq!(owned_parts.to_string(), "segment1/segment2");
157 Ok(())
158 }
159
160 #[test]
161 fn test_parts_iterator() -> anyhow::Result<()> {
162 let parts = Parts::new(vec![Part::new("segment1")?, Part::new("segment2")?]);
163 let collected: Vec<_> = parts.into_iter().collect();
164 assert_eq!(collected.len(), 2);
165 assert_eq!(collected[0].as_str(), "segment1");
166 assert_eq!(collected[1].as_str(), "segment2");
167 Ok(())
168 }
169
170 #[test]
171 fn test_parts_ref_iterator() -> anyhow::Result<()> {
172 let parts = Parts::new(vec![Part::new("segment1")?, Part::new("segment2")?]);
173 let collected: Vec<_> = (&parts).into_iter().map(|p| p.as_str()).collect();
174 assert_eq!(collected, vec!["segment1", "segment2"]);
175 Ok(())
176 }
177
178 #[test]
179 fn test_parts_for_loop() -> anyhow::Result<()> {
180 let parts = Parts::new(vec![Part::new("segment1")?, Part::new("segment2")?]);
181 let mut collected = Vec::new();
182 for part in parts {
183 collected.push(part.as_str().to_string());
184 }
185 assert_eq!(
186 collected,
187 vec!["segment1".to_string(), "segment2".to_string()]
188 );
189 Ok(())
190 }
191 #[test]
192 fn test_parts_validation_max_parts() -> anyhow::Result<()> {
193 let mut parts = Parts::new(vec![]);
195 for i in 0..10 {
196 parts = parts.add_part(Part::new(format!("part{}", i))?)?;
197 }
198
199 let result = parts.add_part(Part::new("one_too_many")?);
201 assert!(result.is_err());
202
203 match result {
204 Err(ErnError::ParseFailure(component, msg)) => {
205 assert_eq!(component, "Parts");
206 assert!(msg.contains("cannot exceed maximum"));
207 }
208 _ => panic!("Expected ParseFailure error for too many parts"),
209 }
210
211 Ok(())
212 }
213}