use std::collections::HashMap;
use url::Url;
use crate::*;
#[derive(Clone)]
pub struct Url2(pub(crate) Box<(Url, Option<HashMap<String, String>>)>);
impl Url2 {
pub fn try_parse<S: AsRef<str>>(s: S) -> Url2Result<Self> {
Ok(Url2::priv_new(Url::parse(s.as_ref())?))
}
pub fn parse<S: AsRef<str>>(s: S) -> Self {
Self::try_parse(s).unwrap()
}
pub fn into_string(self) -> String {
self.into()
}
pub fn query_unique(&mut self) -> Url2QueryUnique {
self.priv_ensure_query_unique_cache();
Url2QueryUnique { url_ref: self }
}
pub fn query_unique_contains_key(&mut self, key: &str) -> bool {
self.priv_ensure_query_unique_cache();
(self.0).1.as_ref().unwrap().contains_key(key)
}
pub fn query_unique_get(&mut self, key: &str) -> Option<&str> {
self.priv_ensure_query_unique_cache();
match (self.0).1.as_ref().unwrap().get(key) {
None => None,
Some(s) => Some(s),
}
}
fn priv_new(url: Url) -> Self {
Self(Box::new((url, None)))
}
fn priv_ensure_query_unique_cache(&mut self) {
if (self.0).1.is_none() {
let _ = std::mem::replace(&mut (self.0).1, Some(HashMap::new()));
for (k, v) in (self.0).0.query_pairs() {
(self.0)
.1
.as_mut()
.unwrap()
.insert(k.to_string(), v.to_string());
}
}
}
pub(crate) fn priv_sync_query_unique_cache(&mut self) {
let mut all = (self.0).1.as_mut().unwrap().drain().collect::<Vec<_>>();
{
let mut pairs = self.query_pairs_mut();
pairs.clear();
for (k, v) in all.iter() {
pairs.append_pair(k, v);
}
}
for (k, v) in all.drain(..) {
(self.0).1.as_mut().unwrap().insert(k, v);
}
}
}
impl serde::Serialize for Url2 {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
(self.0).0.serialize(serializer)
}
}
impl<'de> serde::Deserialize<'de> for Url2 {
fn deserialize<D>(deserializer: D) -> Result<Url2, D::Error>
where
D: serde::Deserializer<'de>,
{
let url: Url = serde::Deserialize::deserialize(deserializer)?;
Ok(Url2::priv_new(url))
}
}
impl std::fmt::Debug for Url2 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Url2")
.field("url", &(self.0).0.as_str())
.finish()
}
}
impl std::cmp::PartialOrd for Url2 {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
(self.0).0.partial_cmp(&(other.0).0)
}
}
impl std::cmp::PartialOrd<Url> for Url2 {
fn partial_cmp(&self, other: &Url) -> Option<std::cmp::Ordering> {
(self.0).0.partial_cmp(other)
}
}
impl std::cmp::Ord for Url2 {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
(self.0).0.cmp(&(other.0).0)
}
}
impl std::cmp::PartialEq for Url2 {
fn eq(&self, other: &Self) -> bool {
(self.0).0.eq(&(other.0).0)
}
}
impl std::cmp::Eq for Url2 {}
impl std::cmp::PartialEq<Url> for Url2 {
fn eq(&self, other: &Url) -> bool {
(self.0).0.eq(&other)
}
}
impl std::hash::Hash for Url2 {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
(self.0).0.hash(state);
}
}
impl std::fmt::Display for Url2 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", (self.0).0)
}
}
impl std::convert::From<Url2> for String {
fn from(url: Url2) -> String {
url.to_string()
}
}
impl std::default::Default for Url2 {
fn default() -> Self {
Url2::priv_new(Url::parse("none:").unwrap())
}
}
impl std::convert::AsRef<str> for Url2 {
fn as_ref(&self) -> &str {
(self.0).0.as_ref()
}
}
impl std::borrow::Borrow<str> for Url2 {
fn borrow(&self) -> &str {
(self.0).0.as_ref()
}
}
impl std::ops::Deref for Url2 {
type Target = Url;
fn deref(&self) -> &Self::Target {
&(self.0).0
}
}
impl std::ops::DerefMut for Url2 {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut (self.0).0
}
}
impl std::borrow::Borrow<Url> for Url2 {
fn borrow(&self) -> &Url {
&(self.0).0
}
}
impl std::borrow::BorrowMut<Url> for Url2 {
fn borrow_mut(&mut self) -> &mut Url {
&mut (self.0).0
}
}
impl std::convert::AsRef<Url> for Url2 {
fn as_ref(&self) -> &Url {
&(self.0).0
}
}
impl std::convert::AsMut<Url> for Url2 {
fn as_mut(&mut self) -> &mut Url {
&mut (self.0).0
}
}
impl std::convert::From<Url> for Url2 {
fn from(url: Url) -> Url2 {
Url2::priv_new(url)
}
}
impl std::convert::From<&Url> for Url2 {
fn from(url: &Url) -> Url2 {
Url2::priv_new(url.clone())
}
}
impl std::convert::From<Url2> for Url {
fn from(url: Url2) -> Url {
(url.0).0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_is_small_for_enum_usage() {
assert_eq!(std::mem::size_of::<usize>(), std::mem::size_of::<Url2>());
}
#[test]
fn it_can_serialize_deserialize() {
let url = Url2::parse("s://u:p@h:42/a/b?a=b&c=d#e");
let json = serde_json::to_string(&url).unwrap();
assert_eq!("\"s://u:p@h:42/a/b?a=b&c=d#e\"", json);
let de: Url2 = serde_json::from_str(&json).unwrap();
assert_eq!(url, de);
}
#[test]
fn it_can_display() {
assert_eq!("test:foo", &format!("{}", Url2::parse("test:foo")));
assert_eq!("test:foo", &Url2::parse("test:foo").into_string());
}
#[test]
fn it_can_parse() {
let url_a = Url2::try_parse("test:bob").unwrap();
let url_b = Url2::parse("test:bob");
let url_c = try_url2!("{}:{}", "test", "bob").unwrap();
let url_d = url2!("{}:{}", "test", "bob");
assert_eq!(url_a, url_b);
assert_eq!(url_a, url_c);
assert_eq!(url_a, url_d);
}
#[test]
fn it_can_convert_from() {
let url = Url2::default();
let url: Url = url.into();
let url: Url2 = url.into();
let url: Url = url.into();
let url: Url2 = (&url).into();
assert_eq!("none:", url.as_str());
}
#[test]
fn it_can_edit_query_unique() {
let mut url = Url2::default();
url.query_unique()
.set_pair("a", "test1")
.set_pair("b", "test2");
assert!(
"none:?a=test1&b=test2" == url.as_str()
|| "none:?b=test2&a=test1" == url.as_str()
);
assert_eq!(true, url.query_unique_contains_key("a"));
assert_eq!(false, url.query_unique_contains_key("c"));
assert_eq!(Some("test1"), url.query_unique_get("a"));
assert_eq!(None, url.query_unique_get("c"));
}
}