Skip to main content

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    /// Set new path while preserving and remapping existing captured segment indices.
97    ///
98    /// The `reindex` closure maps byte indices from the previous path to byte indices in the new
99    /// path.
100    #[doc(hidden)]
101    pub fn update_with_reindex<F>(&mut self, path: T, mut reindex: F)
102    where
103        F: FnMut(u16) -> u16,
104    {
105        self.skip = reindex(self.skip);
106
107        for (_, item) in &mut self.segments {
108            if let PathItem::Segment(start, end) = item {
109                *start = reindex(*start);
110                *end = reindex(*end);
111
112                if *start > *end {
113                    *start = *end;
114                }
115            }
116        }
117
118        self.path = path;
119        let path = self.path.path();
120
121        self.skip = clamp_to_char_boundary(path, self.skip);
122
123        for (_, item) in &mut self.segments {
124            if let PathItem::Segment(start, end) = item {
125                *start = clamp_to_char_boundary(path, *start);
126                *end = clamp_to_char_boundary(path, *end);
127
128                if *start > *end {
129                    *start = *end;
130                }
131            }
132        }
133    }
134
135    /// Reset state.
136    #[inline]
137    pub fn reset(&mut self) {
138        self.skip = 0;
139        self.segments.clear();
140    }
141
142    /// Skip first `n` chars in path.
143    #[inline]
144    pub fn skip(&mut self, n: u16) {
145        self.skip += n;
146    }
147
148    pub(crate) fn add(&mut self, name: impl Into<Cow<'static, str>>, value: PathItem) {
149        match value {
150            PathItem::Static(seg) => self.segments.push((name.into(), PathItem::Static(seg))),
151            PathItem::Segment(begin, end) => self.segments.push((
152                name.into(),
153                PathItem::Segment(self.skip + begin, self.skip + end),
154            )),
155        }
156    }
157
158    #[doc(hidden)]
159    pub fn add_static(
160        &mut self,
161        name: impl Into<Cow<'static, str>>,
162        value: impl Into<Cow<'static, str>>,
163    ) {
164        self.segments
165            .push((name.into(), PathItem::Static(value.into())));
166    }
167
168    /// Check if there are any matched patterns.
169    #[inline]
170    pub fn is_empty(&self) -> bool {
171        self.segments.is_empty()
172    }
173
174    /// Returns number of interpolated segments.
175    #[inline]
176    pub fn segment_count(&self) -> usize {
177        self.segments.len()
178    }
179
180    /// Get matched parameter by name without type conversion
181    pub fn get(&self, name: &str) -> Option<&str> {
182        for (seg_name, val) in self.segments.iter() {
183            if name == seg_name {
184                return match val {
185                    PathItem::Static(ref seg) => Some(seg),
186                    PathItem::Segment(start, end) => {
187                        Some(&self.path.path()[(*start as usize)..(*end as usize)])
188                    }
189                };
190            }
191        }
192
193        None
194    }
195
196    /// Returns matched parameter by name.
197    ///
198    /// If keyed parameter is not available empty string is used as default value.
199    pub fn query(&self, key: &str) -> &str {
200        self.get(key).unwrap_or_default()
201    }
202
203    /// Return iterator to items in parameter container.
204    pub fn iter(&self) -> PathIter<'_, T> {
205        PathIter {
206            idx: 0,
207            params: self,
208        }
209    }
210
211    /// Deserializes matching parameters to a specified type `U`.
212    ///
213    /// # Errors
214    ///
215    /// Returns error when dynamic path segments cannot be deserialized into a `U` type.
216    pub fn load<'de, U: Deserialize<'de>>(&'de self) -> Result<U, de::value::Error> {
217        Deserialize::deserialize(PathDeserializer::new(self))
218    }
219}
220
221fn clamp_to_char_boundary(path: &str, idx: u16) -> u16 {
222    let mut idx = usize::from(idx).min(path.len());
223
224    while idx > 0 && !path.is_char_boundary(idx) {
225        idx -= 1;
226    }
227
228    idx as u16
229}
230
231#[derive(Debug)]
232pub struct PathIter<'a, T> {
233    idx: usize,
234    params: &'a Path<T>,
235}
236
237impl<'a, T: ResourcePath> Iterator for PathIter<'a, T> {
238    type Item = (&'a str, &'a str);
239
240    #[inline]
241    fn next(&mut self) -> Option<(&'a str, &'a str)> {
242        if self.idx < self.params.segment_count() {
243            let idx = self.idx;
244            let res = match self.params.segments[idx].1 {
245                PathItem::Static(ref seg) => seg,
246                PathItem::Segment(start, end) => {
247                    &self.params.path.path()[(start as usize)..(end as usize)]
248                }
249            };
250            self.idx += 1;
251            return Some((&self.params.segments[idx].0, res));
252        }
253        None
254    }
255}
256
257impl<'a, T: ResourcePath> Index<&'a str> for Path<T> {
258    type Output = str;
259
260    fn index(&self, name: &'a str) -> &str {
261        self.get(name)
262            .expect("Value for parameter is not available")
263    }
264}
265
266impl<T: ResourcePath> Index<usize> for Path<T> {
267    type Output = str;
268
269    fn index(&self, idx: usize) -> &str {
270        match self.segments[idx].1 {
271            PathItem::Static(ref seg) => seg,
272            PathItem::Segment(start, end) => &self.path.path()[(start as usize)..(end as usize)],
273        }
274    }
275}
276
277impl<T: ResourcePath> Resource for Path<T> {
278    type Path = T;
279
280    fn resource_path(&mut self) -> &mut Path<Self::Path> {
281        self
282    }
283}
284
285impl<T, P> Resource for T
286where
287    T: DerefMut<Target = Path<P>>,
288    P: ResourcePath,
289{
290    type Path = P;
291
292    fn resource_path(&mut self) -> &mut Path<Self::Path> {
293        &mut *self
294    }
295}
296
297#[cfg(test)]
298mod tests {
299    use std::cell::RefCell;
300
301    use super::*;
302
303    #[allow(clippy::needless_borrow)]
304    #[test]
305    fn deref_impls() {
306        let mut foo = Path::new("/foo");
307        let _ = (&mut foo).resource_path();
308
309        let foo = RefCell::new(foo);
310        let _ = foo.borrow_mut().resource_path();
311    }
312}