From 92f4ba9900d65eb9746a57201ef5135ed0a75876 Mon Sep 17 00:00:00 2001 From: XieJiSS Date: Fri, 13 Sep 2024 16:17:11 +0800 Subject: [PATCH 1/3] feat: support serde --- Cargo.toml | 7 +++++++ src/ascii.rs | 30 ++++++++++++++++++++++++++++++ src/lib.rs | 26 ++++++++++++++++++++++++++ 3 files changed, 63 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index a971276..f009a3e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,3 +22,10 @@ version_check = "0.9" [features] nightly = [] +serde = ["dep:serde"] + +[dependencies] +serde = { version = "1.0.210", optional = true } + +[dev-dependencies] +serde_json = "1.0.128" diff --git a/src/ascii.rs b/src/ascii.rs index c0d35a0..fec7fa2 100644 --- a/src/ascii.rs +++ b/src/ascii.rs @@ -43,6 +43,19 @@ impl Ascii { } } +#[cfg(feature = "serde")] +impl<'de, S> serde::Deserialize<'de> for Ascii +where + S: serde::Deserialize<'de>, +{ + fn deserialize(deserializer: D) -> Result, D::Error> + where + D: serde::Deserializer<'de>, + { + S::deserialize(deserializer).map(Ascii) + } +} + impl Deref for Ascii { type Target = S; #[inline] @@ -165,6 +178,23 @@ mod tests { b.iter(|| assert_eq!(Ascii("foobar"), Ascii("FOOBAR"))); } + #[cfg(feature = "serde")] + #[test] + fn test_ascii_deserialize() { + let a = serde_json::from_str::>("\"foobar\"").unwrap(); + assert_eq!(a, Ascii("foobar")); + let b = serde_json::from_str::>("\"FOOBAR\"").unwrap(); + assert_eq!(b, Ascii("FOOBAR")); + assert_eq!(b, Ascii("foobar")); + assert_eq!(b, a); + assert_ne!(b, Ascii("baz")); + + let c = serde_json::from_str::>("\"baz\"").unwrap(); + assert_eq!(c, Ascii("baz")); + assert_eq!(c, Ascii("Baz")); + assert_eq!(c, Ascii("Baz".to_string())); + } + #[cfg(__unicase__iter_cmp)] #[test] fn test_case_cmp() { diff --git a/src/lib.rs b/src/lib.rs index b676733..e70e23e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -117,6 +117,19 @@ macro_rules! inner { }}; } +#[cfg(feature = "serde")] +impl<'de, S> serde::Deserialize<'de> for UniCase +where + S: serde::Deserialize<'de> + AsRef, +{ + fn deserialize(deserializer: D) -> Result, D::Error> + where + D: serde::Deserializer<'de>, + { + S::deserialize(deserializer).map(UniCase::new) + } +} + impl + Default> Default for UniCase { fn default() -> Self { Self::new(Default::default()) @@ -432,6 +445,19 @@ mod tests { b.iter(|| assert!(::std::str::from_utf8(SUBJECT).is_ok())); } + #[cfg(feature = "serde")] + #[test] + fn test_deserialize() { + let a = serde_json::from_str::>("\"foobar\"").unwrap(); + assert_eq!(a, UniCase::new("foobar")); + let b = serde_json::from_str::>("\"FOOBAR\"").unwrap(); + assert_eq!(b, UniCase::new("FOOBAR")); + assert_eq!(b, UniCase::new("foobar")); + assert_eq!(b, UniCase::new("foobar".to_string())); + assert_eq!(b, a); + assert_ne!(b, UniCase::new("baz")); + } + #[cfg(__unicase__iter_cmp)] #[test] fn test_case_cmp() { From c5c6b98d54e79d117fe8802edf3bf53e57ce4792 Mon Sep 17 00:00:00 2001 From: XieJiSS Date: Fri, 13 Sep 2024 16:37:21 +0800 Subject: [PATCH 2/3] feat: support conversion from AsRef to Ascii --- src/ascii.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/ascii.rs b/src/ascii.rs index fec7fa2..e2b06eb 100644 --- a/src/ascii.rs +++ b/src/ascii.rs @@ -133,6 +133,13 @@ impl FromStr for Ascii { } } +impl> From for Ascii { + #[inline] + fn from(s: S) -> Ascii { + Ascii(s) + } +} + impl> Hash for Ascii { #[inline] fn hash(&self, hasher: &mut H) { @@ -193,6 +200,18 @@ mod tests { assert_eq!(c, Ascii("baz")); assert_eq!(c, Ascii("Baz")); assert_eq!(c, Ascii("Baz".to_string())); + + let mut map = std::collections::HashMap::, i32>::new(); + map.insert("abc".into(), 2); + assert_eq!(map.get(&"abc".into()), Some(&2)); + assert_eq!(map.get(&"ABC".into()), Some(&2)); + assert_eq!(map.len(), 1); + // test if the value is updated correctly using a different cased key + let old = map.insert("ABC".into(), 3); + assert_eq!(old, Some(2)); + assert_eq!(map.get(&"abc".into()), Some(&3)); + assert_eq!(map.get(&"ABC".into()), Some(&3)); + assert_eq!(map.len(), 1); } #[cfg(__unicase__iter_cmp)] From 0cee56531ab893a1ab76fb545b292bb1d8754e19 Mon Sep 17 00:00:00 2001 From: XieJiSS Date: Fri, 13 Sep 2024 16:56:47 +0800 Subject: [PATCH 3/3] feat: also support serializer and more conversions --- src/ascii.rs | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 25 ++++++++++++++++++ 2 files changed, 98 insertions(+) diff --git a/src/ascii.rs b/src/ascii.rs index e2b06eb..c4ddaa2 100644 --- a/src/ascii.rs +++ b/src/ascii.rs @@ -1,3 +1,4 @@ +use alloc::borrow::Cow; use alloc::string::String; #[cfg(__unicase__iter_cmp)] use core::cmp::Ordering; @@ -56,6 +57,19 @@ where } } +#[cfg(feature = "serde")] +impl serde::Serialize for Ascii +where + S: serde::Serialize, +{ + fn serialize(&self, serializer: T) -> Result + where + T: serde::Serializer, + { + self.0.serialize(serializer) + } +} + impl Deref for Ascii { type Target = S; #[inline] @@ -133,6 +147,17 @@ impl FromStr for Ascii { } } +macro_rules! from_impl { + ($from:ty => $to:ty; $by:ident) => ( + impl<'a> From<$from> for Ascii<$to> { + fn from(s: $from) -> Self { + Ascii(s.$by()) + } + } + ); + ($from:ty => $to:ty) => ( from_impl!($from => $to; into); ) +} + impl> From for Ascii { #[inline] fn from(s: S) -> Ascii { @@ -140,6 +165,26 @@ impl> From for Ascii { } } +macro_rules! into_impl { + ($to:ty) => { + impl<'a> Into<$to> for Ascii<$to> { + fn into(self) -> $to { + self.0.into() + } + } + }; +} + +from_impl!(&'a str => Cow<'a, str>); +from_impl!(String => Cow<'a, str>); +from_impl!(&'a str => String); +from_impl!(Cow<'a, str> => String; into_owned); +from_impl!(&'a String => &'a str; as_ref); + +into_impl!(String); +into_impl!(alloc::borrow::Cow<'a, str>); +into_impl!(&'a str); + impl> Hash for Ascii { #[inline] fn hash(&self, hasher: &mut H) { @@ -178,6 +223,25 @@ mod tests { assert_eq!(a, String::from("fooBar")); } + #[test] + fn test_into_impls() { + let a = Ascii("foobar"); + let b: &str = a.into(); + assert_eq!(b, "foobar"); + + let c = Ascii("FOOBAR".to_string()); + let d: String = c.into(); + assert_eq!(d, "FOOBAR"); + + let view: Ascii<&'static str> = Ascii("foobar"); + let _: &'static str = view.into(); + let _: &str = view.into(); + + let owned: Ascii = "foobar".into(); + let _: String = owned.clone().into(); + let _: &str = owned.as_ref(); + } + #[cfg(feature = "nightly")] #[bench] fn bench_ascii_eq(b: &mut ::test::Bencher) { @@ -214,6 +278,15 @@ mod tests { assert_eq!(map.len(), 1); } + #[cfg(feature = "serde")] + #[test] + fn test_ascii_serialize() { + let a = serde_json::to_string(&Ascii("foobar")).unwrap(); + assert_eq!(a, "\"foobar\""); + let b = serde_json::to_string(&Ascii("FOOBAR")).unwrap(); + assert_eq!(b, "\"FOOBAR\""); + } + #[cfg(__unicase__iter_cmp)] #[test] fn test_case_cmp() { diff --git a/src/lib.rs b/src/lib.rs index e70e23e..431bca9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -130,6 +130,19 @@ where } } +#[cfg(feature = "serde")] +impl serde::Serialize for UniCase +where + S: serde::Serialize, +{ + fn serialize(&self, serializer: T) -> Result + where + T: serde::Serializer, + { + inner!(self.0).serialize(serializer) + } +} + impl + Default> Default for UniCase { fn default() -> Self { Self::new(Default::default()) @@ -458,6 +471,18 @@ mod tests { assert_ne!(b, UniCase::new("baz")); } + #[cfg(feature = "serde")] + #[test] + fn test_serialize() { + let a = UniCase::new("foobar"); + let json = serde_json::to_string(&a).unwrap(); + assert_eq!(json, "\"foobar\""); + + let a = UniCase::new("FOOBAR"); + let json = serde_json::to_string(&a).unwrap(); + assert_eq!(json, "\"FOOBAR\""); + } + #[cfg(__unicase__iter_cmp)] #[test] fn test_case_cmp() {