1use std::ops::Index;
2
3use serde::de;
4
5use crate::de::PathDeserializer;
6use crate::{Resource, ResourcePath};
7
8#[derive(Debug, Clone)]
9pub(super) enum PathItem {
10 Static(&'static str),
11 Segment(String),
12 IdxSegment(u32, u32),
13}
14
15#[derive(Debug)]
20pub struct Path<T> {
21 resource: T,
22 pub(super) skip: u32,
23 pub(super) segments: Vec<(&'static str, PathItem)>,
24}
25
26impl<T: Default> Default for Path<T> {
27 fn default() -> Self {
28 Path {
29 resource: T::default(),
30 skip: 0,
31 segments: Vec::new(),
32 }
33 }
34}
35
36impl<T: Clone> Clone for Path<T> {
37 fn clone(&self) -> Self {
38 Path {
39 resource: self.resource.clone(),
40 skip: self.skip,
41 segments: self.segments.clone(),
42 }
43 }
44}
45
46impl<T: ResourcePath> Path<T> {
47 pub fn new(resource: T) -> Path<T> {
49 Path {
50 resource,
51 skip: 0,
52 segments: Vec::new(),
53 }
54 }
55
56 #[inline]
57 pub fn get_ref(&self) -> &T {
59 &self.resource
60 }
61
62 #[inline]
63 pub fn get_mut(&mut self) -> &mut T {
65 &mut self.resource
66 }
67
68 #[inline]
69 pub fn into_inner(self) -> T {
71 self.resource
72 }
73
74 #[inline]
75 pub fn path(&self) -> &str {
81 let skip = self.skip as usize;
82 let path = self.resource.path();
83 if skip <= path.len() { &path[skip..] } else { "" }
84 }
85
86 #[inline]
87 pub fn set(&mut self, resource: T) {
89 self.skip = 0;
90 self.resource = resource;
91 self.segments.clear();
92 }
93
94 #[inline]
95 pub fn reset(&mut self) {
97 self.skip = 0;
98 self.segments.clear();
99 }
100
101 #[inline]
102 pub fn skip(&mut self, n: u32) {
106 let len = u32::try_from(self.resource.path().len()).unwrap_or(u32::MAX);
107 self.skip = self.skip.saturating_add(n).min(len);
108 }
109
110 #[doc(hidden)]
115 pub fn add_static(&mut self, name: &'static str, value: &'static str) {
116 self.segments.push((name, PathItem::Static(value)));
117 }
118
119 #[inline]
120 pub fn is_empty(&self) -> bool {
122 self.segments.is_empty()
123 }
124
125 #[inline]
126 pub fn len(&self) -> usize {
128 self.segments.len()
129 }
130
131 pub fn get(&self, key: &str) -> Option<&str> {
136 for item in &self.segments {
137 if key == item.0 {
138 return match item.1 {
139 PathItem::Static(s) => Some(s),
140 PathItem::Segment(ref s) => Some(s),
141 PathItem::IdxSegment(s, e) => {
142 Some(&self.resource.path()[(s as usize)..(e as usize)])
143 }
144 };
145 }
146 }
147 if key == "tail" { Some(self.path()) } else { None }
148 }
149
150 pub fn iter(&self) -> PathIter<'_, T> {
153 PathIter {
154 idx: 0,
155 params: self,
156 }
157 }
158
159 pub fn load<'de, U: serde::Deserialize<'de>>(&'de self) -> Result<U, de::value::Error> {
163 de::Deserialize::deserialize(PathDeserializer::new(self))
164 }
165}
166
167#[derive(Debug)]
169pub struct PathIter<'a, T> {
170 idx: usize,
171 params: &'a Path<T>,
172}
173
174impl<'a, T: ResourcePath> Iterator for PathIter<'a, T> {
175 type Item = (&'a str, &'a str);
176
177 #[inline]
178 fn next(&mut self) -> Option<(&'a str, &'a str)> {
179 if self.idx < self.params.len() {
180 let idx = self.idx;
181 let res = match self.params.segments[idx].1 {
182 PathItem::Static(s) => s,
183 PathItem::Segment(ref s) => s.as_str(),
184 PathItem::IdxSegment(s, e) => {
185 &self.params.resource.path()[(s as usize)..(e as usize)]
186 }
187 };
188 self.idx += 1;
189 return Some((self.params.segments[idx].0, res));
190 }
191 None
192 }
193}
194
195impl<'a, T: ResourcePath> IntoIterator for &'a Path<T> {
196 type Item = (&'a str, &'a str);
197 type IntoIter = PathIter<'a, T>;
198
199 fn into_iter(self) -> PathIter<'a, T> {
200 self.iter()
201 }
202}
203
204impl<'a, T: ResourcePath> Index<&'a str> for Path<T> {
205 type Output = str;
206
207 fn index(&self, name: &'a str) -> &str {
208 self.get(name)
209 .expect("Value for parameter is not available")
210 }
211}
212
213impl<T: ResourcePath> Index<usize> for Path<T> {
214 type Output = str;
215
216 fn index(&self, idx: usize) -> &str {
217 let Some((_, item)) = self.segments.get(idx) else {
218 panic!(
219 "Segment index {idx} is out of range, path has {} segments",
220 self.segments.len()
221 )
222 };
223 match *item {
224 PathItem::Static(s) => s,
225 PathItem::Segment(ref s) => s,
226 PathItem::IdxSegment(s, e) => &self.resource.path()[(s as usize)..(e as usize)],
227 }
228 }
229}
230
231impl<T: ResourcePath> Resource<T> for Path<T> {
232 fn path(&self) -> &str {
233 self.path()
234 }
235
236 fn resource_path(&mut self) -> &mut Path<T> {
237 self
238 }
239}
240
241#[cfg(test)]
242mod tests {
243 use super::*;
244
245 #[test]
246 fn test_path() {
247 let mut p: Path<String> = Path::default();
248 assert_eq!(p.get_ref(), &String::new());
249 p.get_mut().push_str("test");
250 assert_eq!(p.get_ref().as_str(), "test");
251 let p2 = p.clone();
252 assert_eq!(p2.get_ref().as_str(), "test");
253
254 p.skip(2);
255 assert_eq!(p.get("tail").unwrap(), "st");
256 assert_eq!(p.get("unknown"), None);
257 assert_eq!(p.path(), "st");
258 assert_eq!(p.get_ref().path(), "test");
259
260 p.reset();
261 assert_eq!(p.path(), "test");
262
263 p.segments.push(("k1", PathItem::IdxSegment(0, 2)));
264 assert_eq!(p.get("k1").unwrap(), "te");
265 }
266
267 #[test]
268 fn test_path_set_and_items() {
269 let mut p = Path::new("/first/path");
270 p.skip(7);
271 p.add_static("st", "static");
272 p.segments
273 .push(("seg", PathItem::Segment("owned".to_string())));
274 assert_eq!(p.path(), "path");
275 assert_eq!(p.len(), 2);
276 assert_eq!(p.get("st"), Some("static"));
277 assert_eq!(p.get("seg"), Some("owned"));
278 assert_eq!(&p[0], "static");
279 assert_eq!(&p[1], "owned");
280 assert_eq!(
281 p.iter().collect::<Vec<_>>(),
282 vec![("st", "static"), ("seg", "owned")]
283 );
284
285 p.skip(100);
287 assert_eq!(p.path(), "");
288
289 p.set("/second");
290 assert!(p.is_empty());
291 assert_eq!(p.path(), "/second");
292 assert_eq!(*p.get_ref(), "/second");
293 }
294
295 #[test]
296 #[should_panic(expected = "Value for parameter is not available")]
297 fn test_path_index_missing() {
298 let p = Path::new("/");
299 let _ = &p["missing"];
300 }
301
302 #[test]
303 #[should_panic(expected = "Segment index 1 is out of range, path has 1 segments")]
304 fn index_out_of_range() {
305 let mut p = Path::new("/test");
306 p.add_static("st", "static");
307 assert_eq!(&p[0], "static");
308 let _ = &p[1];
309 }
310}