1use std::collections::HashMap;
2use url::Url;
3
4use crate::*;
5
6#[derive(Clone)]
7pub struct Url2(pub(crate) Box<(Url, Option<HashMap<String, String>>)>);
9
10impl Url2 {
11 pub fn try_parse<S: AsRef<str>>(s: S) -> Url2Result<Self> {
31 Ok(Url2::priv_new(Url::parse(s.as_ref())?))
32 }
33
34 pub fn parse<S: AsRef<str>>(s: S) -> Self {
47 Self::try_parse(s).unwrap()
48 }
49
50 pub fn into_string(self) -> String {
60 self.into()
61 }
62
63 pub fn query_unique(&mut self) -> Url2QueryUnique {
85 self.priv_ensure_query_unique_cache();
86 Url2QueryUnique { url_ref: self }
87 }
88
89 pub fn query_unique_contains_key(&mut self, key: &str) -> bool {
102 self.priv_ensure_query_unique_cache();
103 (self.0).1.as_ref().unwrap().contains_key(key)
104 }
105
106 pub fn query_unique_get(&mut self, key: &str) -> Option<&str> {
125 self.priv_ensure_query_unique_cache();
126 match (self.0).1.as_ref().unwrap().get(key) {
127 None => None,
128 Some(s) => Some(s),
130 }
131 }
132
133 fn priv_new(url: Url) -> Self {
137 Self(Box::new((url, None)))
138 }
139
140 fn priv_ensure_query_unique_cache(&mut self) {
142 if (self.0).1.is_none() {
143 let _ = std::mem::replace(&mut (self.0).1, Some(HashMap::new()));
144 for (k, v) in (self.0).0.query_pairs() {
145 (self.0)
146 .1
147 .as_mut()
148 .unwrap()
149 .insert(k.to_string(), v.to_string());
150 }
151 }
152 }
153
154 pub(crate) fn priv_sync_query_unique_cache(&mut self) {
156 let mut all = (self.0).1.as_mut().unwrap().drain().collect::<Vec<_>>();
157 {
158 let mut pairs = self.query_pairs_mut();
159 pairs.clear();
160 for (k, v) in all.iter() {
161 pairs.append_pair(k, v);
162 }
163 }
164 for (k, v) in all.drain(..) {
165 (self.0).1.as_mut().unwrap().insert(k, v);
166 }
167 }
168}
169
170impl serde::Serialize for Url2 {
171 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
172 where
173 S: serde::Serializer,
174 {
175 (self.0).0.serialize(serializer)
176 }
177}
178
179impl<'de> serde::Deserialize<'de> for Url2 {
180 fn deserialize<D>(deserializer: D) -> Result<Url2, D::Error>
181 where
182 D: serde::Deserializer<'de>,
183 {
184 let url: Url = serde::Deserialize::deserialize(deserializer)?;
185 Ok(Url2::priv_new(url))
186 }
187}
188
189impl std::fmt::Debug for Url2 {
190 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
191 f.debug_struct("Url2")
192 .field("url", &(self.0).0.as_str())
193 .finish()
194 }
195}
196
197impl std::cmp::PartialOrd for Url2 {
198 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
199 (self.0).0.partial_cmp(&(other.0).0)
200 }
201}
202
203impl std::cmp::PartialOrd<Url> for Url2 {
204 fn partial_cmp(&self, other: &Url) -> Option<std::cmp::Ordering> {
205 (self.0).0.partial_cmp(other)
206 }
207}
208
209impl std::cmp::Ord for Url2 {
210 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
211 (self.0).0.cmp(&(other.0).0)
212 }
213}
214
215impl std::cmp::PartialEq for Url2 {
216 fn eq(&self, other: &Self) -> bool {
217 (self.0).0.eq(&(other.0).0)
218 }
219}
220
221impl std::cmp::Eq for Url2 {}
222
223impl std::cmp::PartialEq<Url> for Url2 {
224 fn eq(&self, other: &Url) -> bool {
225 (self.0).0.eq(&other)
226 }
227}
228
229impl std::hash::Hash for Url2 {
230 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
231 (self.0).0.hash(state);
232 }
233}
234
235impl std::fmt::Display for Url2 {
236 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
237 write!(f, "{}", (self.0).0)
238 }
239}
240
241impl std::convert::From<Url2> for String {
242 fn from(url: Url2) -> String {
243 url.to_string()
244 }
245}
246
247impl std::default::Default for Url2 {
248 fn default() -> Self {
249 Url2::priv_new(Url::parse("none:").unwrap())
250 }
251}
252
253impl std::convert::AsRef<str> for Url2 {
254 fn as_ref(&self) -> &str {
255 (self.0).0.as_ref()
256 }
257}
258
259impl std::borrow::Borrow<str> for Url2 {
260 fn borrow(&self) -> &str {
261 (self.0).0.as_ref()
262 }
263}
264
265impl std::ops::Deref for Url2 {
266 type Target = Url;
267
268 fn deref(&self) -> &Self::Target {
269 &(self.0).0
270 }
271}
272
273impl std::ops::DerefMut for Url2 {
274 fn deref_mut(&mut self) -> &mut Self::Target {
275 &mut (self.0).0
276 }
277}
278
279impl std::borrow::Borrow<Url> for Url2 {
280 fn borrow(&self) -> &Url {
281 &(self.0).0
282 }
283}
284
285impl std::borrow::BorrowMut<Url> for Url2 {
286 fn borrow_mut(&mut self) -> &mut Url {
287 &mut (self.0).0
288 }
289}
290
291impl std::convert::AsRef<Url> for Url2 {
292 fn as_ref(&self) -> &Url {
293 &(self.0).0
294 }
295}
296
297impl std::convert::AsMut<Url> for Url2 {
298 fn as_mut(&mut self) -> &mut Url {
299 &mut (self.0).0
300 }
301}
302
303impl std::convert::From<Url> for Url2 {
304 fn from(url: Url) -> Url2 {
305 Url2::priv_new(url)
306 }
307}
308
309impl std::convert::From<&Url> for Url2 {
310 fn from(url: &Url) -> Url2 {
311 Url2::priv_new(url.clone())
312 }
313}
314
315impl std::convert::From<Url2> for Url {
316 fn from(url: Url2) -> Url {
317 (url.0).0
318 }
319}
320
321#[cfg(test)]
322mod tests {
323 use super::*;
324
325 #[test]
326 fn it_is_small_for_enum_usage() {
327 assert_eq!(std::mem::size_of::<usize>(), std::mem::size_of::<Url2>());
328 }
329
330 #[test]
331 fn it_can_serialize_deserialize() {
332 let url = Url2::parse("s://u:p@h:42/a/b?a=b&c=d#e");
333 let json = serde_json::to_string(&url).unwrap();
334 assert_eq!("\"s://u:p@h:42/a/b?a=b&c=d#e\"", json);
335 let de: Url2 = serde_json::from_str(&json).unwrap();
336 assert_eq!(url, de);
337 }
338
339 #[test]
340 fn it_can_display() {
341 assert_eq!("test:foo", &format!("{}", Url2::parse("test:foo")));
342 assert_eq!("test:foo", &Url2::parse("test:foo").into_string());
343 }
344
345 #[test]
346 fn it_can_parse() {
347 let url_a = Url2::try_parse("test:bob").unwrap();
348 let url_b = Url2::parse("test:bob");
349 let url_c = try_url2!("{}:{}", "test", "bob").unwrap();
350 let url_d = url2!("{}:{}", "test", "bob");
351 assert_eq!(url_a, url_b);
352 assert_eq!(url_a, url_c);
353 assert_eq!(url_a, url_d);
354 }
355
356 #[test]
357 fn it_can_convert_from() {
358 let url = Url2::default();
359 let url: Url = url.into();
360 let url: Url2 = url.into();
361 let url: Url = url.into();
362 let url: Url2 = (&url).into();
363 assert_eq!("none:", url.as_str());
364 }
365
366 #[test]
367 fn it_can_edit_query_unique() {
368 let mut url = Url2::default();
369 url.query_unique()
370 .set_pair("a", "test1")
371 .set_pair("b", "test2");
372 assert!(
373 "none:?a=test1&b=test2" == url.as_str()
374 || "none:?b=test2&a=test1" == url.as_str()
375 );
376 assert_eq!(true, url.query_unique_contains_key("a"));
377 assert_eq!(false, url.query_unique_contains_key("c"));
378 assert_eq!(Some("test1"), url.query_unique_get("a"));
379 assert_eq!(None, url.query_unique_get("c"));
380 }
381}