macro_toolset/string/
urlencoding.rs

1//! URL Encoded string
2
3use super::{StringExtT, StringT};
4use crate::wrapper;
5
6#[macro_export]
7/// See [`Encode`] or [`Decode`] for more information.
8///
9/// # Example
10///
11/// ```
12/// # use macro_toolset::{urlencoding_str, string::StringExtT};
13/// let data = urlencoding_str!(E: "你好, 世界").to_string_ext();
14/// assert_eq!(data, "%E4%BD%A0%E5%A5%BD%2C%20%E4%B8%96%E7%95%8C");
15/// let data = urlencoding_str!(D: "%E4%BD%A0%E5%A5%BD%2C%20%E4%B8%96%E7%95%8C").to_string_ext();
16/// assert_eq!(data, "你好, 世界");
17/// ```
18macro_rules! urlencoding_str {
19    (E: $data:expr) => {
20        $crate::string::urlencoding::Encode { inner: $data }
21    };
22    (D: $data:expr) => {
23        $crate::string::urlencoding::Decode { inner: $data }
24    };
25}
26
27wrapper! {
28    #[derive(Debug)]
29    /// `string` which is to be encoded.
30    pub Encode<T>(pub T)
31}
32
33impl<T> StringT for Encode<T>
34where
35    T: StringT,
36{
37    #[inline]
38    fn encode_to_buf(self, string: &mut Vec<u8>) {
39        let mut buf = Vec::with_capacity(64);
40        self.inner.encode_to_buf(&mut buf);
41
42        string.reserve_exact(buf.len() | 15);
43        buf.into_iter().for_each(|byte| {
44            if matches!(byte, b'0'..=b'9' | b'A'..=b'Z' | b'a'..=b'z' |  b'-' | b'.' | b'_' | b'~')
45            {
46                string.push(byte);
47            } else {
48                string.extend([b'%', to_hex_digit(byte >> 4), to_hex_digit(byte & 15)]);
49            }
50        });
51    }
52
53    #[inline]
54    fn encode_to_buf_with_separator(self, string: &mut Vec<u8>, separator: &str) {
55        self.encode_to_buf(string);
56        string.extend(separator.as_bytes());
57    }
58
59    #[inline]
60    fn encode_to_bytes_buf(self, string: &mut bytes::BytesMut) {
61        let mut buf = Vec::with_capacity(64);
62        self.inner.encode_to_buf(&mut buf);
63
64        string.reserve(buf.len() | 15);
65        buf.into_iter().for_each(|byte| {
66            if matches!(byte, b'0'..=b'9' | b'A'..=b'Z' | b'a'..=b'z' |  b'-' | b'.' | b'_' | b'~')
67            {
68                string.extend(Some(byte));
69            } else {
70                string.extend([b'%', to_hex_digit(byte >> 4), to_hex_digit(byte & 15)]);
71            }
72        });
73    }
74
75    #[inline]
76    fn encode_to_bytes_buf_with_separator(self, string: &mut bytes::BytesMut, separator: &str) {
77        self.encode_to_bytes_buf(string);
78        string.extend(separator.as_bytes());
79    }
80}
81
82impl<T> StringExtT for Encode<T> where T: StringT {}
83
84#[inline(always)]
85const fn to_hex_digit(digit: u8) -> u8 {
86    match digit {
87        0..=9 => b'0' + digit,
88        10..=255 => b'A' - 10 + digit,
89    }
90}
91
92wrapper! {
93    #[derive(Debug)]
94    /// `string` which is to be decoded.
95    pub Decode<T>(pub T)
96}
97
98impl<T> StringT for Decode<T>
99where
100    T: AsRef<str>,
101{
102    #[inline]
103    fn encode_to_buf(self, string: &mut Vec<u8>) {
104        let encoded_bytes = self.inner.as_ref().as_bytes();
105
106        let mut encoded_bytes_iter = encoded_bytes.split(|&c| c == b'%');
107        if let Some(non_escaped_part) = encoded_bytes_iter.next() {
108            string.extend(non_escaped_part);
109        } else {
110            return;
111        }
112        for escaped_part in encoded_bytes_iter {
113            let decoded = escaped_part.get(0..=1).and_then(|escaped_part| {
114                Some((
115                    from_hex_digit(escaped_part[0])?,
116                    from_hex_digit(escaped_part[1])?,
117                ))
118            });
119            if let Some(decoded) = decoded {
120                string.push((decoded.0 << 4) | decoded.1);
121                if let Some(non_escaped_part) = escaped_part.get(2..) {
122                    string.extend(non_escaped_part);
123                }
124            } else {
125                // Error, keep it.
126                string.extend(b"%");
127                string.extend(escaped_part);
128            }
129        }
130    }
131
132    #[inline]
133    fn encode_to_buf_with_separator(self, string: &mut Vec<u8>, separator: &str) {
134        self.encode_to_buf(string);
135        string.extend(separator.as_bytes());
136    }
137
138    #[inline]
139    fn encode_to_bytes_buf(self, string: &mut bytes::BytesMut) {
140        let encoded_bytes = self.inner.as_ref().as_bytes();
141
142        let mut encoded_bytes_iter = encoded_bytes.split(|&c| c == b'%');
143        if let Some(non_escaped_part) = encoded_bytes_iter.next() {
144            string.extend(non_escaped_part);
145        } else {
146            return;
147        }
148        for escaped_part in encoded_bytes_iter {
149            let decoded = escaped_part.get(0..=1).and_then(|escaped_part| {
150                Some((
151                    from_hex_digit(escaped_part[0])?,
152                    from_hex_digit(escaped_part[1])?,
153                ))
154            });
155            if let Some(decoded) = decoded {
156                string.extend(Some((decoded.0 << 4) | decoded.1));
157                if let Some(non_escaped_part) = escaped_part.get(2..) {
158                    string.extend(non_escaped_part);
159                }
160            } else {
161                // Error, keep it.
162                string.extend(b"%");
163                string.extend(escaped_part);
164            }
165        }
166    }
167
168    #[inline]
169    fn encode_to_bytes_buf_with_separator(self, string: &mut bytes::BytesMut, separator: &str) {
170        self.encode_to_bytes_buf(string);
171        string.extend(separator.as_bytes());
172    }
173}
174
175impl<T> StringExtT for Decode<T> where T: AsRef<str> {}
176
177#[inline(always)]
178const fn from_hex_digit(digit: u8) -> Option<u8> {
179    match digit {
180        b'0'..=b'9' => Some(digit - b'0'),
181        b'A'..=b'F' => Some(digit - b'A' + 10),
182        b'a'..=b'f' => Some(digit - b'a' + 10),
183        _ => None,
184    }
185}