scrappy_router/
path.rs

1use std::ops::Index;
2
3use serde::de;
4
5use crate::de::PathDeserializer;
6use crate::{Resource, ResourcePath};
7
8#[derive(Debug, Clone, Copy)]
9pub(crate) enum PathItem {
10    Static(&'static str),
11    Segment(u16, u16),
12}
13
14/// Resource path match information
15///
16/// If resource path contains variable patterns, `Path` stores them.
17#[derive(Debug)]
18pub struct Path<T> {
19    path: T,
20    pub(crate) skip: u16,
21    pub(crate) segments: Vec<(&'static str, PathItem)>,
22}
23
24impl<T: Default> Default for Path<T> {
25    fn default() -> Self {
26        Path {
27            path: T::default(),
28            skip: 0,
29            segments: Vec::new(),
30        }
31    }
32}
33
34impl<T: Clone> Clone for Path<T> {
35    fn clone(&self) -> Self {
36        Path {
37            path: self.path.clone(),
38            skip: self.skip,
39            segments: self.segments.clone(),
40        }
41    }
42}
43
44impl<T: ResourcePath> Path<T> {
45    pub fn new(path: T) -> Path<T> {
46        Path {
47            path,
48            skip: 0,
49            segments: Vec::new(),
50        }
51    }
52
53    #[inline]
54    /// Get reference to inner path instance
55    pub fn get_ref(&self) -> &T {
56        &self.path
57    }
58
59    #[inline]
60    /// Get mutable reference to inner path instance
61    pub fn get_mut(&mut self) -> &mut T {
62        &mut self.path
63    }
64
65    #[inline]
66    /// Path
67    pub fn path(&self) -> &str {
68        let skip = self.skip as usize;
69        let path = self.path.path();
70        if skip <= path.len() {
71            &path[skip..]
72        } else {
73            ""
74        }
75    }
76
77    #[inline]
78    /// Set new path
79    pub fn set(&mut self, path: T) {
80        self.skip = 0;
81        self.path = path;
82        self.segments.clear();
83    }
84
85    #[inline]
86    /// Reset state
87    pub fn reset(&mut self) {
88        self.skip = 0;
89        self.segments.clear();
90    }
91
92    #[inline]
93    /// Skip first `n` chars in path
94    pub fn skip(&mut self, n: u16) {
95        self.skip += n;
96    }
97
98    pub(crate) fn add(&mut self, name: &'static str, value: PathItem) {
99        match value {
100            PathItem::Static(s) => self.segments.push((name, PathItem::Static(s))),
101            PathItem::Segment(begin, end) => self
102                .segments
103                .push((name, PathItem::Segment(self.skip + begin, self.skip + end))),
104        }
105    }
106
107    #[doc(hidden)]
108    pub fn add_static(&mut self, name: &'static str, value: &'static str) {
109        self.segments.push((name, PathItem::Static(value)));
110    }
111
112    #[inline]
113    /// Check if there are any matched patterns
114    pub fn is_empty(&self) -> bool {
115        self.segments.is_empty()
116    }
117
118    #[inline]
119    /// Check number of extracted parameters
120    pub fn len(&self) -> usize {
121        self.segments.len()
122    }
123
124    /// Get matched parameter by name without type conversion
125    pub fn get(&self, key: &str) -> Option<&str> {
126        for item in self.segments.iter() {
127            if key == item.0 {
128                return match item.1 {
129                    PathItem::Static(ref s) => Some(&s),
130                    PathItem::Segment(s, e) => {
131                        Some(&self.path.path()[(s as usize)..(e as usize)])
132                    }
133                };
134            }
135        }
136        if key == "tail" {
137            Some(&self.path.path()[(self.skip as usize)..])
138        } else {
139            None
140        }
141    }
142
143    /// Get unprocessed part of the path
144    pub fn unprocessed(&self) -> &str {
145        &self.path.path()[(self.skip as usize)..]
146    }
147
148    /// Get matched parameter by name.
149    ///
150    /// If keyed parameter is not available empty string is used as default
151    /// value.
152    pub fn query(&self, key: &str) -> &str {
153        if let Some(s) = self.get(key) {
154            s
155        } else {
156            ""
157        }
158    }
159
160    /// Return iterator to items in parameter container
161    pub fn iter(&self) -> PathIter<T> {
162        PathIter {
163            idx: 0,
164            params: self,
165        }
166    }
167
168    /// Try to deserialize matching parameters to a specified type `U`
169    pub fn load<'de, U: serde::Deserialize<'de>>(&'de self) -> Result<U, de::value::Error> {
170        de::Deserialize::deserialize(PathDeserializer::new(self))
171    }
172}
173
174#[derive(Debug)]
175pub struct PathIter<'a, T> {
176    idx: usize,
177    params: &'a Path<T>,
178}
179
180impl<'a, T: ResourcePath> Iterator for PathIter<'a, T> {
181    type Item = (&'a str, &'a str);
182
183    #[inline]
184    fn next(&mut self) -> Option<(&'a str, &'a str)> {
185        if self.idx < self.params.len() {
186            let idx = self.idx;
187            let res = match self.params.segments[idx].1 {
188                PathItem::Static(ref s) => &s,
189                PathItem::Segment(s, e) => &self.params.path.path()[(s as usize)..(e as usize)],
190            };
191            self.idx += 1;
192            return Some((&self.params.segments[idx].0, res));
193        }
194        None
195    }
196}
197
198impl<'a, T: ResourcePath> Index<&'a str> for Path<T> {
199    type Output = str;
200
201    fn index(&self, name: &'a str) -> &str {
202        self.get(name)
203            .expect("Value for parameter is not available")
204    }
205}
206
207impl<T: ResourcePath> Index<usize> for Path<T> {
208    type Output = str;
209
210    fn index(&self, idx: usize) -> &str {
211        match self.segments[idx].1 {
212            PathItem::Static(ref s) => &s,
213            PathItem::Segment(s, e) => &self.path.path()[(s as usize)..(e as usize)],
214        }
215    }
216}
217
218impl<T: ResourcePath> Resource<T> for Path<T> {
219    fn resource_path(&mut self) -> &mut Self {
220        self
221    }
222}