Skip to main content

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