matchit/
escape.rs

1use std::{fmt, ops::Range};
2
3/// An unescaped route that keeps track of the position of escaped characters ('{{' or '}}').
4///
5/// Note that this type dereferences to `&[u8]`.
6#[derive(Clone, Default)]
7pub struct UnescapedRoute {
8    // The raw unescaped route.
9    inner: Vec<u8>,
10    escaped: Vec<usize>,
11}
12
13impl UnescapedRoute {
14    /// Unescapes escaped brackets ('{{' or '}}') in a route.
15    pub fn new(mut inner: Vec<u8>) -> UnescapedRoute {
16        let mut escaped = Vec::new();
17        let mut i = 0;
18
19        while let Some(&c) = inner.get(i) {
20            if (c == b'{' && inner.get(i + 1) == Some(&b'{'))
21                || (c == b'}' && inner.get(i + 1) == Some(&b'}'))
22            {
23                inner.remove(i);
24                escaped.push(i);
25            }
26
27            i += 1;
28        }
29
30        UnescapedRoute { inner, escaped }
31    }
32
33    /// Returns true if the character at the given index was escaped.
34    pub fn is_escaped(&self, i: usize) -> bool {
35        self.escaped.contains(&i)
36    }
37
38    /// Replaces the characters in the given range.
39    pub fn splice(
40        &mut self,
41        range: Range<usize>,
42        replace: Vec<u8>,
43    ) -> impl Iterator<Item = u8> + '_ {
44        // Ignore any escaped characters in the range being replaced.
45        self.escaped.retain(|x| !range.contains(x));
46
47        // Update the escaped indices.
48        let offset = (replace.len() as isize) - (range.len() as isize);
49        for i in &mut self.escaped {
50            if *i > range.end {
51                *i = i.checked_add_signed(offset).unwrap();
52            }
53        }
54
55        self.inner.splice(range, replace)
56    }
57
58    /// Appends another route to the end of this one.
59    pub fn append(&mut self, other: &UnescapedRoute) {
60        for i in &other.escaped {
61            self.escaped.push(self.inner.len() + i);
62        }
63
64        self.inner.extend_from_slice(&other.inner);
65    }
66
67    /// Truncates the route to the given length.
68    pub fn truncate(&mut self, to: usize) {
69        self.escaped.retain(|&x| x < to);
70        self.inner.truncate(to);
71    }
72
73    /// Returns a reference to this route.
74    pub fn as_ref(&self) -> UnescapedRef<'_> {
75        UnescapedRef {
76            inner: &self.inner,
77            escaped: &self.escaped,
78            offset: 0,
79        }
80    }
81
82    /// Returns a reference to the unescaped slice.
83    pub fn unescaped(&self) -> &[u8] {
84        &self.inner
85    }
86
87    /// Returns the unescaped route.
88    pub fn into_unescaped(self) -> Vec<u8> {
89        self.inner
90    }
91}
92
93impl std::ops::Deref for UnescapedRoute {
94    type Target = [u8];
95
96    fn deref(&self) -> &Self::Target {
97        &self.inner
98    }
99}
100
101impl fmt::Debug for UnescapedRoute {
102    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103        fmt::Debug::fmt(std::str::from_utf8(&self.inner).unwrap(), f)
104    }
105}
106
107/// A reference to an `UnescapedRoute`.
108#[derive(Copy, Clone)]
109pub struct UnescapedRef<'a> {
110    pub inner: &'a [u8],
111    escaped: &'a [usize],
112    // An offset applied to each escaped index.
113    offset: isize,
114}
115
116impl<'a> UnescapedRef<'a> {
117    /// Converts this reference into an owned route.
118    pub fn to_owned(self) -> UnescapedRoute {
119        let mut escaped = Vec::new();
120        for &i in self.escaped {
121            let i = i.checked_add_signed(self.offset);
122
123            match i {
124                Some(i) if i < self.inner.len() => escaped.push(i),
125                _ => {}
126            }
127        }
128
129        UnescapedRoute {
130            escaped,
131            inner: self.inner.to_owned(),
132        }
133    }
134
135    /// Returns `true` if the character at the given index was escaped.
136    pub fn is_escaped(&self, i: usize) -> bool {
137        if let Some(i) = i.checked_add_signed(-self.offset) {
138            return self.escaped.contains(&i);
139        }
140
141        false
142    }
143
144    /// Slices the route with `start..`.
145    pub fn slice_off(&self, start: usize) -> UnescapedRef<'a> {
146        UnescapedRef {
147            inner: &self.inner[start..],
148            escaped: self.escaped,
149            offset: self.offset - (start as isize),
150        }
151    }
152
153    /// Slices the route with `..end`.
154    pub fn slice_until(&self, end: usize) -> UnescapedRef<'a> {
155        UnescapedRef {
156            inner: &self.inner[..end],
157            escaped: self.escaped,
158            offset: self.offset,
159        }
160    }
161
162    /// Returns a reference to the unescaped slice.
163    pub fn unescaped(&self) -> &[u8] {
164        self.inner
165    }
166}
167
168impl<'a> std::ops::Deref for UnescapedRef<'a> {
169    type Target = &'a [u8];
170
171    fn deref(&self) -> &Self::Target {
172        &self.inner
173    }
174}
175
176impl<'a> fmt::Debug for UnescapedRef<'a> {
177    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178        f.debug_struct("UnescapedRef")
179            .field("inner", &std::str::from_utf8(self.inner))
180            .field("escaped", &self.escaped)
181            .field("offset", &self.offset)
182            .finish()
183    }
184}