macro_toolset/string/
urlencoding.rs1use super::{StringExtT, StringT};
4use crate::wrapper;
5
6#[macro_export]
7macro_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 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 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 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 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}