diff --git a/Cargo.lock b/Cargo.lock index 9ea173f808b..d345f8828ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -502,15 +502,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "deduplicating_array" -version = "0.5.0" -dependencies = [ - "postcard", - "serde", - "serde_json", -] - [[package]] name = "derivative" version = "2.2.0" @@ -1150,7 +1141,7 @@ dependencies = [ "serde", "serde-tuple-vec-map", "serde_json", - "serde_with", + "serde_utils", "smallvec", "tinystr", "writeable", @@ -1183,7 +1174,6 @@ name = "icu_list" version = "0.1.0" dependencies = [ "criterion", - "deduplicating_array", "displaydoc", "icu_benchmark_macros", "icu_locid", @@ -1194,6 +1184,7 @@ dependencies = [ "regex-automata 0.1.10 (git+https://github.com/burntsushi/regex-automata?rev=d8eee1279fac79514f6e366b6976f97ad7b37174)", "serde", "serde_json", + "serde_utils", "writeable", "zerovec", ] @@ -2275,12 +2266,6 @@ dependencies = [ "semver 1.0.4", ] -[[package]] -name = "rustversion" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f" - [[package]] name = "ryu" version = "1.0.5" @@ -2466,13 +2451,12 @@ dependencies = [ ] [[package]] -name = "serde_with" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad6056b4cb69b6e43e3a0f055def223380baecc99da683884f205bf347f7c4b3" +name = "serde_utils" +version = "0.5.0" dependencies = [ - "rustversion", + "postcard", "serde", + "serde_json", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 2921abbc806..1ff263d532a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,7 @@ members = [ "tools/benchmark/binsize", "tools/datagen", "utils/codepointtrie", - "utils/deduplicating_array", + "utils/serde_utils", "utils/fixed_decimal", "utils/litemap", "utils/pattern", diff --git a/components/datetime/Cargo.toml b/components/datetime/Cargo.toml index dc3f8ac188e..da63b582c43 100644 --- a/components/datetime/Cargo.toml +++ b/components/datetime/Cargo.toml @@ -46,7 +46,7 @@ smallvec = "1.6" displaydoc = { version = "0.2.3", default-features = false } either = { version = "1.6.1", default-features = false } num_enum = { version = "0.5", default_features = false } -serde_with = { version = "1.11.0", default_features = false, optional = true} +serde_utils = { version = "0.5", path = "../../utils/serde_utils", optional = true } [dev-dependencies] criterion = "0.3" @@ -68,7 +68,7 @@ bench = false # This option is required for Benchmark CI std = ["icu_provider/std", "icu_locid/std", "icu_calendar/std"] default = ["provider_serde"] bench = [] -provider_serde = ["serde", "litemap/serde_serialize", "smallvec/serde", "litemap/serde", "zerovec/serde", "tinystr/serde", "serde_with"] +provider_serde = ["serde", "litemap/serde_serialize", "smallvec/serde", "litemap/serde", "zerovec/serde", "tinystr/serde", "serde_utils"] provider_transform_internals = ["std"] [[bench]] diff --git a/components/datetime/src/provider/calendar/symbols.rs b/components/datetime/src/provider/calendar/symbols.rs index d0fbef0c387..2951f413266 100644 --- a/components/datetime/src/provider/calendar/symbols.rs +++ b/components/datetime/src/provider/calendar/symbols.rs @@ -96,7 +96,7 @@ symbols!( pub struct SymbolsV1<'data>( #[cfg_attr( feature = "provider_serde", - serde(borrow, with = "serde_with::As::<[serde_with::BorrowCow; 12]>") + serde(borrow) )] pub [Cow<'data, str>; 12], ); @@ -107,7 +107,7 @@ symbols!( pub struct SymbolsV1<'data>( #[cfg_attr( feature = "provider_serde", - serde(borrow, with = "serde_with::As::<[serde_with::BorrowCow; 7]>") + serde(borrow) )] pub [Cow<'data, str>; 7], ); @@ -122,12 +122,12 @@ symbols!( pub pm: Cow<'data, str>, #[cfg_attr( feature = "provider_serde", - serde(borrow, with = "serde_with::As::>") + serde(borrow, deserialize_with = "serde_utils::option_of_cow::deserialize") )] pub noon: Option>, #[cfg_attr( feature = "provider_serde", - serde(borrow, with = "serde_with::As::>") + serde(borrow, deserialize_with = "serde_utils::option_of_cow::deserialize") )] pub midnight: Option>, } diff --git a/experimental/list_formatter/Cargo.toml b/experimental/list_formatter/Cargo.toml index c7bd06e6128..06aa26e2548 100644 --- a/experimental/list_formatter/Cargo.toml +++ b/experimental/list_formatter/Cargo.toml @@ -25,7 +25,7 @@ icu_locid = { version = "0.4", path = "../../components/locid" } icu_provider = { version = "0.4", path = "../../provider/core", features = ["macros"] } serde = { version = "1.0", default-features = false, features = ["derive", "alloc"], optional = true } zerovec = { version = "0.5", path = "../../utils/zerovec", features = ["yoke"] } -deduplicating_array = { version = "0.5", path = "../../utils/deduplicating_array", optional = true } +serde_utils = { version = "0.5", path = "../../utils/serde_utils", optional = true } regex-automata = { version = "0.1", git = "https://github.com/burntsushi/regex-automata", rev = "d8eee1279fac79514f6e366b6976f97ad7b37174", default-features = false } writeable = { version = "0.2.1", path = "../../utils/writeable" } @@ -43,6 +43,6 @@ path = "src/lib.rs" [features] default = ["icu4x_human_readable_de"] std = ["icu_provider/std", "icu_locid/std", "regex-automata/std"] -provider_serde = ["serde", "zerovec/serde", "deduplicating_array"] +provider_serde = ["serde", "zerovec/serde", "serde_utils"] provider_transform_internals = ["provider_serde", "std"] icu4x_human_readable_de = ["provider_serde", "regex-automata/alloc"] diff --git a/experimental/list_formatter/src/provider.rs b/experimental/list_formatter/src/provider.rs index 70dd11092dd..a8f93715c55 100644 --- a/experimental/list_formatter/src/provider.rs +++ b/experimental/list_formatter/src/provider.rs @@ -37,7 +37,7 @@ pub mod key { pub struct ListFormatterPatternsV1<'data>( #[cfg_attr( feature = "provider_serde", - serde(borrow, with = "deduplicating_array") + serde(borrow, with = "serde_utils::deduplicating_array") )] /// The patterns in the order start, middle, end, pair, short_start, short_middle, /// short_end, short_pair, narrow_start, narrow_middle, narrow_end, narrow_pair, diff --git a/utils/deduplicating_array/Cargo.toml b/utils/serde_utils/Cargo.toml similarity index 84% rename from utils/deduplicating_array/Cargo.toml rename to utils/serde_utils/Cargo.toml index 430c1c1391f..5a858f47587 100644 --- a/utils/deduplicating_array/Cargo.toml +++ b/utils/serde_utils/Cargo.toml @@ -3,8 +3,8 @@ # (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). [package] -name = "deduplicating_array" -description = "A serde serialization strategy that uses PartialEq to reduce serialized size." +name = "serde_utils" +description = "Various Serde serialization and deserialization strategies." version = "0.5.0" authors = ["The ICU4X Project Developers"] edition = "2018" @@ -34,4 +34,4 @@ bench = [] std = [] [[example]] -name = "postcard" +name = "deduplicate" diff --git a/utils/deduplicating_array/LICENSE b/utils/serde_utils/LICENSE similarity index 100% rename from utils/deduplicating_array/LICENSE rename to utils/serde_utils/LICENSE diff --git a/utils/deduplicating_array/README.md b/utils/serde_utils/README.md similarity index 100% rename from utils/deduplicating_array/README.md rename to utils/serde_utils/README.md diff --git a/utils/deduplicating_array/examples/postcard.rs b/utils/serde_utils/examples/deduplicate.rs similarity index 96% rename from utils/deduplicating_array/examples/postcard.rs rename to utils/serde_utils/examples/deduplicate.rs index 4e4a825daad..cd0dff2399c 100644 --- a/utils/deduplicating_array/examples/postcard.rs +++ b/utils/serde_utils/examples/deduplicate.rs @@ -8,7 +8,7 @@ #[derive(serde::Serialize, serde::Deserialize)] struct DataStruct { - #[serde(with = "deduplicating_array")] + #[serde(with = "serde_utils::deduplicating_array")] coordinates: [(f64, f64); 5], } diff --git a/utils/serde_utils/src/array_of_cow.rs b/utils/serde_utils/src/array_of_cow.rs new file mode 100644 index 00000000000..cb4b610c583 --- /dev/null +++ b/utils/serde_utils/src/array_of_cow.rs @@ -0,0 +1,3 @@ +// This file is part of ICU4X. For terms of use, please see the file +// called LICENSE at the top level of the ICU4X source tree +// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). \ No newline at end of file diff --git a/utils/deduplicating_array/src/lib.rs b/utils/serde_utils/src/deduplicating_array.rs similarity index 99% rename from utils/deduplicating_array/src/lib.rs rename to utils/serde_utils/src/deduplicating_array.rs index c6cc68a8ec4..bca11c15dbe 100644 --- a/utils/deduplicating_array/src/lib.rs +++ b/utils/serde_utils/src/deduplicating_array.rs @@ -12,7 +12,7 @@ //! //! #[derive(serde::Deserialize, serde::Serialize)] //! pub struct Foo { -//! #[serde(with = "deduplicating_array")] +//! #[serde(with = "serde_utils::deduplicating_array")] //! data: [Bar; 12], //! // ... //! } @@ -28,9 +28,6 @@ //! This implies that singleton integer arrays cannot be used as array elements (they do work in Bincode, //! but there's really not much point in using them). -#![no_std] -extern crate alloc; - use alloc::fmt::{Error, Formatter}; use alloc::format; use serde::de::{Deserialize, Deserializer, EnumAccess, Visitor}; diff --git a/utils/serde_utils/src/lib.rs b/utils/serde_utils/src/lib.rs new file mode 100644 index 00000000000..71ea6a36056 --- /dev/null +++ b/utils/serde_utils/src/lib.rs @@ -0,0 +1,6 @@ +#![no_std] +extern crate alloc; + +pub mod deduplicating_array; +pub mod array_of_cow; +pub mod option_of_cow; diff --git a/utils/serde_utils/src/option_of_cow.rs b/utils/serde_utils/src/option_of_cow.rs new file mode 100644 index 00000000000..c9d7b810ba5 --- /dev/null +++ b/utils/serde_utils/src/option_of_cow.rs @@ -0,0 +1,122 @@ +// This file is part of ICU4X. For terms of use, please see the file +// called LICENSE at the top level of the ICU4X source tree +// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). + +use alloc::borrow::Cow; +use alloc::fmt; +use alloc::string::String; +use alloc::borrow::ToOwned; + +pub fn deserialize<'de, D>(deserializer: D) -> Result>, D::Error> +where + D: serde::de::Deserializer<'de> +{ + use serde::de::Error; + use serde::de::Unexpected; + + // Note: The following visitor is taken from serde::private::de::borrow_cow_str + struct CowStrVisitor; + + impl<'a> serde::de::Visitor<'a> for CowStrVisitor { + type Value = Cow<'a, str>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a string") + } + + fn visit_str(self, v: &str) -> Result + where + E: Error, + { + Ok(Cow::Owned(v.to_owned())) + } + + fn visit_borrowed_str(self, v: &'a str) -> Result + where + E: Error, + { + Ok(Cow::Borrowed(v)) + } + + fn visit_string(self, v: String) -> Result + where + E: Error, + { + Ok(Cow::Owned(v)) + } + + fn visit_bytes(self, v: &[u8]) -> Result + where + E: Error, + { + match alloc::str::from_utf8(v) { + Ok(s) => Ok(Cow::Owned(s.to_owned())), + Err(_) => Err(Error::invalid_value(Unexpected::Bytes(v), &self)), + } + } + + fn visit_borrowed_bytes(self, v: &'a [u8]) -> Result + where + E: Error, + { + match alloc::str::from_utf8(v) { + Ok(s) => Ok(Cow::Borrowed(s)), + Err(_) => Err(Error::invalid_value(Unexpected::Bytes(v), &self)), + } + } + + fn visit_byte_buf(self, v: alloc::vec::Vec) -> Result + where + E: Error, + { + match String::from_utf8(v) { + Ok(s) => Ok(Cow::Owned(s)), + Err(e) => Err(Error::invalid_value( + Unexpected::Bytes(&e.into_bytes()), + &self, + )), + } + } + } + + struct OptionCowStrVisitor; + + impl<'a> serde::de::Visitor<'a> for OptionCowStrVisitor { + type Value = Option>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a string or null") + } + + fn visit_none(self) -> Result + where + E: Error, + { + Ok(None) + } + + fn visit_some(self, deserializer: D) -> Result + where + D: serde::de::Deserializer<'a>, + { + Ok(Some(deserializer.deserialize_str(CowStrVisitor)?)) + } + } + + deserializer.deserialize_option(OptionCowStrVisitor) +} + +#[test] +fn test() { + #[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)] + struct Demo<'s>( + #[serde(borrow, deserialize_with = "deserialize")] + Option>, + ); + + let data_orig = Demo(Some("Hello world".into())); + let bytes = postcard::to_stdvec(&data_orig).expect("serialize"); + let data_new = postcard::from_bytes::(&bytes).expect("deserialize"); + assert_eq!(data_orig, data_new); + assert!(matches!(data_new.0, Some(Cow::Borrowed(_)))); +}