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        self.get(key).unwrap_or_default()
151    }
152
153    /// Return iterator to items in parameter container
154    pub fn iter(&self) -> PathIter<'_, T> {
155        PathIter {
156            idx: 0,
157            params: self,
158        }
159    }
160
161    /// Try to deserialize matching parameters to a specified type `U`
162    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/// Iterator to items in parameter container
168#[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.path.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> Index<&'a str> for Path<T> {
196    type Output = str;
197
198    fn index(&self, name: &'a str) -> &str {
199        self.get(name)
200            .expect("Value for parameter is not available")
201    }
202}
203
204impl<T: ResourcePath> Index<usize> for Path<T> {
205    type Output = str;
206
207    fn index(&self, idx: usize) -> &str {
208        match self.segments[idx].1 {
209            PathItem::Static(s) => s,
210            PathItem::Segment(ref s) => s,
211            PathItem::IdxSegment(s, e) => &self.path.path()[(s as usize)..(e as usize)],
212        }
213    }
214}
215
216impl<T: ResourcePath> Resource<T> for Path<T> {
217    fn path(&self) -> &str {
218        self.path()
219    }
220
221    fn resource_path(&mut self) -> &mut Path<T> {
222        self
223    }
224}
225
226#[cfg(test)]
227mod tests {
228    use super::*;
229
230    #[test]
231    fn test_path() {
232        let mut p: Path<String> = Path::default();
233        assert_eq!(p.get_ref(), &String::new());
234        p.get_mut().push_str("test");
235        assert_eq!(p.get_ref().as_str(), "test");
236        let p2 = p.clone();
237        assert_eq!(p2.get_ref().as_str(), "test");
238
239        p.skip(2);
240        assert_eq!(p.get("tail").unwrap(), "st");
241        assert_eq!(p.get("unknown"), None);
242        assert_eq!(p.query("tail"), "st");
243        assert_eq!(p.query("unknown"), "");
244        assert_eq!(p.unprocessed(), "st");
245
246        p.reset();
247        assert_eq!(p.unprocessed(), "test");
248
249        p.segments.push(("k1", PathItem::IdxSegment(0, 2)));
250        assert_eq!(p.get("k1").unwrap(), "te");
251    }
252}