1use std::{
2 borrow::Borrow,
3 cmp::Ordering,
4 fmt::{self, Display, Formatter},
5 hash::{Hash, Hasher},
6 str::FromStr,
7};
8
9use crate::{Encoder, InvalidPctString, PctStr};
10
11pub struct PctString(Vec<u8>);
17
18impl PctString {
19 pub fn new<B: Into<Vec<u8>>>(bytes: B) -> Result<Self, InvalidPctString<Vec<u8>>> {
24 let bytes = bytes.into();
25 if PctStr::validate(bytes.iter().copied()) {
26 Ok(Self(bytes))
27 } else {
28 Err(InvalidPctString(bytes))
29 }
30 }
31
32 pub fn from_string(string: String) -> Result<Self, InvalidPctString<String>> {
33 Self::new(string).map_err(|e| {
34 e.map(|bytes| unsafe {
35 String::from_utf8_unchecked(bytes)
37 })
38 })
39 }
40
41 pub unsafe fn new_unchecked<B: Into<Vec<u8>>>(bytes: B) -> Self {
47 Self(bytes.into())
48 }
49
50 pub fn encode<E: Encoder>(src: impl Iterator<Item = char>, encoder: E) -> PctString {
66 use std::fmt::Write;
67
68 let mut buf = String::with_capacity(4);
69 let mut encoded = String::new();
70 for c in src {
71 if encoder.encode(c) || c == '%' {
72 buf.clear();
73 buf.push(c);
74 for byte in buf.bytes() {
75 write!(encoded, "%{:02X}", byte).unwrap();
76 }
77 } else {
78 encoded.push(c);
79 }
80 }
81
82 PctString(encoded.into_bytes())
83 }
84
85 #[inline]
87 pub fn as_pct_str(&self) -> &PctStr {
88 unsafe {
89 PctStr::new_unchecked(&self.0)
91 }
92 }
93
94 #[inline]
96 pub fn into_string(self) -> String {
97 unsafe {
98 String::from_utf8_unchecked(self.0)
101 }
102 }
103
104 #[inline]
105 pub fn into_bytes(self) -> Vec<u8> {
106 self.0
107 }
108}
109
110impl std::ops::Deref for PctString {
111 type Target = PctStr;
112
113 #[inline]
114 fn deref(&self) -> &PctStr {
115 self.as_pct_str()
116 }
117}
118
119impl Borrow<PctStr> for PctString {
120 fn borrow(&self) -> &PctStr {
121 self.as_pct_str()
122 }
123}
124
125impl AsRef<PctStr> for PctString {
126 fn as_ref(&self) -> &PctStr {
127 self.as_pct_str()
128 }
129}
130
131impl AsRef<str> for PctString {
132 fn as_ref(&self) -> &str {
133 self.as_str()
134 }
135}
136
137impl AsRef<[u8]> for PctString {
138 fn as_ref(&self) -> &[u8] {
139 self.as_bytes()
140 }
141}
142
143impl PartialEq for PctString {
144 #[inline]
145 fn eq(&self, other: &PctString) -> bool {
146 let mut a = self.chars();
147 let mut b = other.chars();
148
149 loop {
150 match (a.next(), b.next()) {
151 (Some(a), Some(b)) if a != b => return false,
152 (Some(_), None) => return false,
153 (None, Some(_)) => return false,
154 (None, None) => break,
155 _ => (),
156 }
157 }
158
159 true
160 }
161}
162
163impl Eq for PctString {}
164
165impl PartialEq<PctStr> for PctString {
166 #[inline]
167 fn eq(&self, other: &PctStr) -> bool {
168 let mut a = self.chars();
169 let mut b = other.chars();
170
171 loop {
172 match (a.next(), b.next()) {
173 (Some(a), Some(b)) if a != b => return false,
174 (Some(_), None) => return false,
175 (None, Some(_)) => return false,
176 (None, None) => break,
177 _ => (),
178 }
179 }
180
181 true
182 }
183}
184
185impl PartialEq<&str> for PctString {
186 #[inline]
187 fn eq(&self, other: &&str) -> bool {
188 let mut a = self.chars();
189 let mut b = other.chars();
190
191 loop {
192 match (a.next(), b.next()) {
193 (Some(a), Some(b)) if a != b => return false,
194 (Some(_), None) => return false,
195 (None, Some(_)) => return false,
196 (None, None) => break,
197 _ => (),
198 }
199 }
200
201 true
202 }
203}
204
205impl PartialEq<str> for PctString {
206 #[inline]
207 fn eq(&self, other: &str) -> bool {
208 self.eq(&other)
209 }
210}
211
212impl PartialOrd for PctString {
213 fn partial_cmp(&self, other: &PctString) -> Option<Ordering> {
214 self.as_pct_str().partial_cmp(other.as_pct_str())
215 }
216}
217
218impl PartialOrd<PctStr> for PctString {
219 fn partial_cmp(&self, other: &PctStr) -> Option<Ordering> {
220 self.as_pct_str().partial_cmp(other)
221 }
222}
223
224impl Hash for PctString {
225 #[inline]
226 fn hash<H: Hasher>(&self, hasher: &mut H) {
227 for c in self.chars() {
228 c.hash(hasher)
229 }
230 }
231}
232
233impl Display for PctString {
234 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
235 Display::fmt(self.as_str(), f)
236 }
237}
238
239impl fmt::Debug for PctString {
240 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
241 fmt::Debug::fmt(self.as_str(), f)
242 }
243}
244
245impl FromStr for PctString {
246 type Err = InvalidPctString<String>;
247
248 fn from_str(s: &str) -> Result<Self, Self::Err> {
249 Self::from_string(s.to_string())
250 }
251}
252
253impl TryFrom<String> for PctString {
254 type Error = InvalidPctString<String>;
255
256 fn try_from(value: String) -> Result<Self, Self::Error> {
257 Self::from_string(value)
258 }
259}
260
261impl<'a> TryFrom<&'a str> for PctString {
262 type Error = InvalidPctString<String>;
263
264 fn try_from(value: &'a str) -> Result<Self, Self::Error> {
265 Self::from_string(value.to_owned())
266 }
267}
268
269impl<'a> TryFrom<&'a str> for &'a PctStr {
270 type Error = InvalidPctString<&'a str>;
271
272 fn try_from(value: &'a str) -> Result<Self, Self::Error> {
273 PctStr::new(value)
274 }
275}