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
12pub const DEFAULT_MAX_PARTS: usize = 10;
24
25#[derive(new, Debug, PartialEq, Clone, Eq, Default, PartialOrd)]
27#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
28pub struct Parts(pub(crate) Vec<Part>);
29
30impl Parts {
31 pub fn add_part<T>(self, part: T) -> Result<Self, ErnError>
42 where
43 T: Into<Part>,
44 {
45 self.add_part_with_limit(part, DEFAULT_MAX_PARTS)
46 }
47
48 pub fn add_part_with_limit<T>(mut self, part: T, max_parts: usize) -> Result<Self, ErnError>
77 where
78 T: Into<Part>,
79 {
80 if self.0.len() >= max_parts {
82 return Err(ErnError::ParseFailure(
83 "Parts",
84 format!("cannot exceed maximum of {max_parts} parts"),
85 ));
86 }
87
88 self.0.push(part.into());
89 Ok(self)
90 }
91
92 pub fn into_owned(self) -> Parts {
94 Parts(self.0.into_iter().collect())
95 }
96
97 pub fn len(&self) -> usize {
99 self.0.len()
100 }
101
102 pub fn is_empty(&self) -> bool {
104 self.0.is_empty()
105 }
106}
107
108impl Hash for Parts {
109 fn hash<H: Hasher>(&self, state: &mut H) {
110 self.0.len().hash(state);
111 for part in &self.0 {
112 part.hash(state);
113 }
114 }
115}
116
117impl FromIterator<Part> for Parts {
118 fn from_iter<T: IntoIterator<Item = Part>>(iter: T) -> Self {
119 Parts(iter.into_iter().collect())
120 }
121}
122
123impl fmt::Display for Parts {
124 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126 write!(
127 f,
128 "{}",
129 self.0
130 .iter()
131 .map(|p| p.as_str())
132 .collect::<Vec<_>>()
133 .join("/")
134 )
135 }
136}
137
138impl IntoIterator for Parts {
139 type Item = Part;
140 type IntoIter = std::vec::IntoIter<Self::Item>;
141
142 fn into_iter(self) -> Self::IntoIter {
143 self.0.into_iter()
144 }
145}
146
147impl<'a> IntoIterator for &'a Parts {
148 type Item = &'a Part;
149 type IntoIter = std::slice::Iter<'a, Part>;
150
151 fn into_iter(self) -> Self::IntoIter {
152 self.0.iter()
153 }
154}
155
156#[cfg(test)]
157mod tests {
158 use super::*;
159
160 #[test]
161 fn test_parts_creation() -> anyhow::Result<()> {
162 let parts = Parts::new(vec![Part::new("segment1")?, Part::new("segment2")?]);
163 assert_eq!(parts.to_string(), "segment1/segment2");
164 Ok(())
165 }
166
167 #[test]
168 fn test_parts_add_part() -> anyhow::Result<()> {
169 let mut parts = Parts::new(vec![Part::new("segment1")?]);
170 parts = parts.add_part(Part::new("segment2")?)?;
171 parts = parts.add_part(Part::new("segment3")?)?;
172
173 assert_eq!(parts.to_string(), "segment1/segment2/segment3");
174 Ok(())
175 }
176
177 #[test]
178 fn test_parts_from_iterator() -> anyhow::Result<()> {
179 let parts: Result<Parts, _> = vec!["segment1", "segment2", "segment3"]
180 .into_iter()
181 .map(Part::new)
182 .collect();
183 match parts {
184 Ok(parts) => {
185 assert_eq!(parts.to_string(), "segment1/segment2/segment3");
186 Ok(())
187 }
188 Err(e) => Err(anyhow::anyhow!(e)),
189 }
190 }
191
192 #[test]
193 fn test_parts_into_owned() -> anyhow::Result<()> {
194 let parts = Parts::new(vec![Part::new("segment1")?, Part::new("segment2")?]);
195 let owned_parts: Parts = parts;
196 assert_eq!(owned_parts.to_string(), "segment1/segment2");
197 Ok(())
198 }
199
200 #[test]
201 fn test_parts_iterator() -> anyhow::Result<()> {
202 let parts = Parts::new(vec![Part::new("segment1")?, Part::new("segment2")?]);
203 let collected: Vec<_> = parts.into_iter().collect();
204 assert_eq!(collected.len(), 2);
205 assert_eq!(collected[0].as_str(), "segment1");
206 assert_eq!(collected[1].as_str(), "segment2");
207 Ok(())
208 }
209
210 #[test]
211 fn test_parts_ref_iterator() -> anyhow::Result<()> {
212 let parts = Parts::new(vec![Part::new("segment1")?, Part::new("segment2")?]);
213 let collected: Vec<_> = (&parts).into_iter().map(|p| p.as_str()).collect();
214 assert_eq!(collected, vec!["segment1", "segment2"]);
215 Ok(())
216 }
217
218 #[test]
219 fn test_parts_for_loop() -> anyhow::Result<()> {
220 let parts = Parts::new(vec![Part::new("segment1")?, Part::new("segment2")?]);
221 let mut collected = Vec::new();
222 for part in parts {
223 collected.push(part.as_str().to_string());
224 }
225 assert_eq!(
226 collected,
227 vec!["segment1".to_string(), "segment2".to_string()]
228 );
229 Ok(())
230 }
231 #[test]
232 fn test_parts_validation_max_parts() -> anyhow::Result<()> {
233 let mut parts = Parts::new(vec![]);
235 for i in 0..10 {
236 parts = parts.add_part(Part::new(format!("part{}", i))?)?;
237 }
238
239 let result = parts.add_part(Part::new("one_too_many")?);
241 assert!(result.is_err());
242
243 match result {
244 Err(ErnError::ParseFailure(component, msg)) => {
245 assert_eq!(component, "Parts");
246 assert!(msg.contains("cannot exceed maximum"));
247 }
248 _ => panic!("Expected ParseFailure error for too many parts"),
249 }
250
251 Ok(())
252 }
253}