serde_querystring/
decode.rs

1use std::borrow::{Borrow, Cow};
2
3/// Parses a single percent encoded char
4#[inline]
5pub fn parse_char(h: u8, l: u8) -> Option<u8> {
6    Some(char::from(h).to_digit(16)? as u8 * 0x10 + char::from(l).to_digit(16)? as u8)
7}
8
9/// Decodes a slice and return a Reference pointer
10pub fn parse_bytes<'de, 's>(
11    slice: &'de [u8],
12    scratch: &'s mut Vec<u8>,
13) -> Reference<'de, 's, [u8]> {
14    scratch.clear();
15
16    // Index of the last byte we copied to scratch
17    let mut index = 0;
18
19    // Index of the first byte not yet copied into the scratch space.
20    let mut cursor = 0;
21
22    while let Some(v) = slice.get(cursor) {
23        match v {
24            b'+' => {
25                scratch.extend_from_slice(&slice[index..cursor]);
26                scratch.push(b' ');
27
28                cursor += 1;
29                index = cursor;
30            }
31            b'%' => {
32                // we saw percentage
33                if slice.len() > cursor + 2 {
34                    match parse_char(slice[cursor + 1], slice[cursor + 2]) {
35                        Some(b) => {
36                            scratch.extend_from_slice(&slice[index..cursor]);
37                            scratch.push(b);
38
39                            cursor += 3;
40                            index = cursor;
41                        }
42                        None => {
43                            // If it wasn't valid, go to the next byte
44                            cursor += 1;
45                        }
46                    }
47                } else {
48                    cursor += 1;
49                }
50            }
51            _ => {
52                cursor += 1;
53            }
54        }
55    }
56
57    if scratch.is_empty() {
58        Reference::Borrowed(&slice[index..cursor])
59    } else {
60        scratch.extend_from_slice(&slice[index..cursor]);
61        Reference::Copied(scratch)
62    }
63}
64
65/// A struct that can hold an owned or borrowed value
66///
67/// The difference between `Reference` and `Cow` is that it can contain a reference
68/// to either a slice present in the input(Borrowed), or a slice(decoded) present in the scratch(Copied)
69pub enum Reference<'b, 'c, T>
70where
71    T: ?Sized + 'static + ToOwned,
72{
73    Borrowed(&'b T),
74    Copied(&'c T),
75    Owned(<T as ToOwned>::Owned),
76}
77
78impl<'b, 'c, T> Reference<'b, 'c, T>
79where
80    T: ?Sized + ToOwned + 'static,
81{
82    pub fn into_cow(self) -> Cow<'b, T> {
83        match self {
84            Reference::Borrowed(b) => Cow::Borrowed(b),
85            Reference::Copied(c) => Cow::Owned(c.to_owned()),
86            Reference::Owned(o) => Cow::Owned(o),
87        }
88    }
89
90    pub fn try_map<F, B, E>(self, f: F) -> Result<Reference<'b, 'c, B>, E>
91    where
92        F: FnOnce(&T) -> Result<&B, E>,
93        B: ?Sized + ToOwned + 'static,
94    {
95        match self {
96            Reference::Borrowed(b) => f(b).map(Reference::Borrowed),
97            Reference::Copied(c) => f(c).map(Reference::Copied),
98            Reference::Owned(o) => f(o.borrow()).map(|o| Reference::Owned(o.to_owned())),
99        }
100    }
101}
102
103impl<'b, 'c, T> std::ops::Deref for Reference<'b, 'c, T>
104where
105    T: ?Sized + 'static + ToOwned,
106{
107    type Target = T;
108
109    fn deref(&self) -> &Self::Target {
110        match *self {
111            Reference::Borrowed(b) => b,
112            Reference::Copied(c) => c,
113            Reference::Owned(ref o) => o.borrow(),
114        }
115    }
116}