rocket_http/parse/
indexed.rs

1#![allow(dead_code)]
2
3use std::borrow::Cow;
4use std::ops::{Index, Range};
5use std::fmt::{self, Debug};
6
7use pear::input::Length;
8
9use crate::ext::IntoOwned;
10
11pub use pear::input::Extent;
12
13pub type IndexedStr<'a> = Indexed<'a, str>;
14pub type IndexedBytes<'a> = Indexed<'a, [u8]>;
15
16pub trait AsPtr {
17    fn as_ptr(&self) -> *const u8;
18}
19
20impl AsPtr for str {
21    #[inline(always)]
22    fn as_ptr(&self) -> *const u8 {
23        str::as_ptr(self)
24    }
25}
26
27impl AsPtr for [u8] {
28    #[inline(always)]
29    fn as_ptr(&self) -> *const u8 {
30        <[u8]>::as_ptr(self)
31    }
32}
33
34/// Either a concrete string or indices to the start and end of a string.
35#[derive(PartialEq)]
36pub enum Indexed<'a, T: ?Sized + ToOwned> {
37    /// The start and end index of a string.
38    Indexed(usize, usize),
39    /// A concrete string.
40    Concrete(Cow<'a, T>)
41}
42
43impl<A, T: ?Sized + ToOwned> From<Extent<A>> for Indexed<'_, T> {
44    fn from(e: Extent<A>) -> Self {
45        Indexed::Indexed(e.start, e.end)
46    }
47}
48
49impl<'a, T: ?Sized + ToOwned + 'a> From<Cow<'a, T>> for Indexed<'a, T> {
50    #[inline(always)]
51    fn from(value: Cow<'a, T>) -> Indexed<'a, T> {
52        Indexed::Concrete(value)
53    }
54}
55
56impl<'a, T: ?Sized + ToOwned + 'a> Indexed<'a, T> {
57    /// Panics if `self` is not an `Indexed`.
58    #[inline(always)]
59    pub fn indices(self) -> (usize, usize) {
60        match self {
61            Indexed::Indexed(a, b) => (a, b),
62            _ => panic!("cannot convert indexed T to U unless indexed")
63        }
64    }
65
66    /// Panics if `self` is not an `Indexed`.
67    #[inline(always)]
68    pub fn coerce<U: ?Sized + ToOwned>(self) -> Indexed<'a, U> {
69        match self {
70            Indexed::Indexed(a, b) => Indexed::Indexed(a, b),
71            _ => panic!("cannot convert indexed T to U unless indexed")
72        }
73    }
74
75    /// Panics if `self` is not an `Indexed`.
76    #[inline(always)]
77    pub fn coerce_lifetime<'b>(self) -> Indexed<'b, T> {
78        match self {
79            Indexed::Indexed(a, b) => Indexed::Indexed(a, b),
80            _ => panic!("cannot coerce lifetime unless indexed")
81        }
82    }
83}
84
85impl<T: 'static + ?Sized + ToOwned> IntoOwned for Indexed<'_, T> {
86    type Owned = Indexed<'static, T>;
87
88    fn into_owned(self) -> Indexed<'static, T> {
89        match self {
90            Indexed::Indexed(a, b) => Indexed::Indexed(a, b),
91            Indexed::Concrete(cow) => Indexed::Concrete(IntoOwned::into_owned(cow))
92        }
93    }
94}
95
96use std::ops::Add;
97
98impl<'a, T: ?Sized + ToOwned + 'a> Add for Indexed<'a, T> {
99    type Output = Indexed<'a, T>;
100
101    #[inline]
102    fn add(self, other: Indexed<'a, T>) -> Indexed<'a, T> {
103        match self {
104            Indexed::Indexed(a, b) => match other {
105                Indexed::Indexed(c, d) if b == c && a < d => Indexed::Indexed(a, d),
106                _ => panic!("+ requires indexed")
107            }
108            _ => panic!("+ requires indexed")
109        }
110    }
111}
112
113impl<'a, T: ?Sized + ToOwned + 'a> Indexed<'a, T>
114    where T: Length + AsPtr + Index<Range<usize>, Output = T>
115{
116    /// Returns `None` if `needle` is not a substring of `haystack`. Otherwise
117    /// returns an `Indexed` with the indices of `needle` in `haystack`.
118    pub fn checked_from(needle: &T, haystack: &T) -> Option<Indexed<'a, T>> {
119        let needle_start = needle.as_ptr() as usize;
120        let haystack_start = haystack.as_ptr() as usize;
121        if needle_start < haystack_start {
122            return None;
123        }
124
125        let needle_end = needle_start + needle.len();
126        let haystack_end = haystack_start + haystack.len();
127        if needle_end > haystack_end {
128            return None;
129        }
130
131        let start = needle_start - haystack_start;
132        let end = start + needle.len();
133        Some(Indexed::Indexed(start, end))
134    }
135
136    /// Like `checked_from` but without checking if `needle` is indeed a
137    /// substring of `haystack`.
138    ///
139    /// # Safety
140    ///
141    /// The caller must ensure that `needle` is indeed a substring of
142    /// `haystack`.
143    pub unsafe fn unchecked_from(needle: &T, haystack: &T) -> Indexed<'a, T> {
144        let haystack_start = haystack.as_ptr() as usize;
145        let needle_start = needle.as_ptr() as usize;
146
147        let start = needle_start - haystack_start;
148        let end = start + needle.len();
149        Indexed::Indexed(start, end)
150    }
151
152    /// Whether this string is derived from indexes or not.
153    #[inline]
154    pub fn is_indexed(&self) -> bool {
155        match *self {
156            Indexed::Indexed(..) => true,
157            Indexed::Concrete(..) => false,
158        }
159    }
160
161    /// Whether this string is empty.
162    #[inline]
163    pub fn is_empty(&self) -> bool {
164        self.len() == 0
165    }
166
167    /// Make `self` concrete by allocating if indexed.
168    ///
169    /// # Panics
170    ///
171    /// Panics if `self` is an indexed string and `source` is None.
172    pub fn into_concrete(self, source: &Option<Cow<'_, T>>) -> Cow<'a, T> {
173        if self.is_indexed() && source.is_none() {
174            panic!("cannot concretize indexed str to str without base string!")
175        }
176
177        match self {
178            Indexed::Indexed(i, j) => Cow::Owned(source.as_ref().unwrap()[i..j].to_owned()),
179            Indexed::Concrete(string) => string,
180        }
181    }
182
183    /// Retrieves the string `self` corresponds to. If `self` is derived from
184    /// indexes, the corresponding subslice of `source` is returned. Otherwise,
185    /// the concrete string is returned.
186    ///
187    /// # Panics
188    ///
189    /// Panics if `self` is an indexed string and `source` is None.
190    pub fn from_cow_source<'s>(&'s self, source: &'s Option<Cow<'_, T>>) -> &'s T {
191        if self.is_indexed() && source.is_none() {
192            panic!("cannot convert indexed str to str without base string!")
193        }
194
195        match *self {
196            Indexed::Indexed(i, j) => &source.as_ref().unwrap()[i..j],
197            Indexed::Concrete(ref mstr) => mstr.as_ref(),
198        }
199    }
200
201    /// Retrieves the string `self` corresponds to. If `self` is derived from
202    /// indexes, the corresponding subslice of `string` is returned. Otherwise,
203    /// the concrete string is returned.
204    ///
205    /// # Panics
206    ///
207    /// Panics if `self` is an indexed string and `string` is None.
208    pub fn from_source<'s>(&'s self, source: Option<&'s T>) -> &'s T {
209        if self.is_indexed() && source.is_none() {
210            panic!("Cannot convert indexed str to str without base string!")
211        }
212
213        match *self {
214            Indexed::Indexed(i, j) => &source.unwrap()[(i as usize)..(j as usize)],
215            Indexed::Concrete(ref mstr) => &*mstr,
216        }
217    }
218}
219
220impl<'a, T: ToOwned + ?Sized + 'a> Clone for Indexed<'a, T> {
221    fn clone(&self) -> Self {
222        match *self {
223            Indexed::Indexed(a, b) => Indexed::Indexed(a, b),
224            Indexed::Concrete(ref cow) => Indexed::Concrete(cow.clone())
225        }
226    }
227}
228
229impl<'a, T: ?Sized + 'a> Debug for Indexed<'a, T>
230    where T: ToOwned + Debug, T::Owned: Debug
231{
232    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
233        match *self {
234            Indexed::Indexed(a, b) => fmt::Debug::fmt(&(a, b), f),
235            Indexed::Concrete(ref cow) => fmt::Debug::fmt(cow, f),
236        }
237    }
238}
239
240impl<'a, T: ?Sized + Length + ToOwned + 'a> Length for Indexed<'a, T> {
241    #[inline(always)]
242    fn len(&self) -> usize {
243        match *self {
244            Indexed::Indexed(a, b) => (b - a) as usize,
245            Indexed::Concrete(ref cow) => cow.len()
246        }
247    }
248}