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