actix_router/
path.rs

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