diff --git a/Cargo.toml b/Cargo.toml index 89e47117d..5e328f8c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,9 +5,16 @@ edition = "2021" [package.metadata.docs.rs] # To build locally: -# RUSTDOCFLAGS="--cfg doc_cfg" RUSTC_BOOTSTRAP=1 cargo doc --all-features --no-deps --open +# RUSTDOCFLAGS="--cfg=doc_cfg -Zunstable-options --generate-link-to-definition" RUSTC_BOOTSTRAP=1 cargo doc --all-features --no-deps --open all-features = true -rustdoc-args = ["--cfg", "doc_cfg"] +rustdoc-args = [ + # Enable doc_cfg showing the required features. + "--cfg=doc_cfg", + + # Generate links to definition in rustdoc source code pages + # https://github.com/rust-lang/rust/pull/84176 + "-Zunstable-options", "--generate-link-to-definition" +] [features] default = [] @@ -20,13 +27,14 @@ hex = "0.4.3" # Serialization and Deserialization support serde = { version = "1.0.197", optional = true } serde_derive = { version = "1.0.197", optional = true } -serde_with = { version = "3.6.1", optional = true } +serde_with = { version = "3.6.1", default-features = false, optional = true } # RNG support rand_core = { version = "0.6.4", optional = true } [dev-dependencies] serde_json = "*" +bcs = "*" [target.wasm32-unknown-unknown.dev-dependencies] wasm-bindgen-test = "0.3" diff --git a/src/lib.rs b/src/lib.rs index e4ec9f6be..b06a4b6ef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,10 +2,6 @@ pub mod types; - -#[cfg(feature = "serde")] -mod serde_readable; - pub fn add(left: usize, right: usize) -> usize { left + right } diff --git a/src/serde_readable.rs b/src/serde_readable.rs deleted file mode 100644 index 8d12f76b8..000000000 --- a/src/serde_readable.rs +++ /dev/null @@ -1,56 +0,0 @@ -use std::marker::PhantomData; -use serde_with::{SerializeAs, DeserializeAs}; -use serde::{Deserializer, Serializer}; - -/// Use with serde_as to control serde for human-readable serialization and deserialization -/// `H` : serde_as SerializeAs/DeserializeAs delegation for human readable in/output -/// `R` : serde_as SerializeAs/DeserializeAs delegation for non-human readable in/output -/// -/// # Example: -/// -/// ```text -/// #[serde_as] -/// #[derive(Deserialize, Serialize)] -/// struct Example(#[serde_as(as = "Readable")] [u8; 20]); -/// ``` -/// -/// The above example will delegate human-readable serde to `DisplayFromStr` -/// and array tuple (default) for non-human-readable serializer. -pub struct Readable { - human_readable: PhantomData, - non_human_readable: PhantomData, -} - -impl SerializeAs for Readable -where - H: SerializeAs, - R: SerializeAs, -{ - fn serialize_as(value: &T, serializer: S) -> Result - where - S: Serializer, - { - if serializer.is_human_readable() { - H::serialize_as(value, serializer) - } else { - R::serialize_as(value, serializer) - } - } -} - -impl<'de, R, H, T> DeserializeAs<'de, T> for Readable -where - H: DeserializeAs<'de, T>, - R: DeserializeAs<'de, T>, -{ - fn deserialize_as(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - if deserializer.is_human_readable() { - H::deserialize_as(deserializer) - } else { - R::deserialize_as(deserializer) - } - } -} diff --git a/src/types/address.rs b/src/types/address.rs index b176399f5..1baf9c2f6 100644 --- a/src/types/address.rs +++ b/src/types/address.rs @@ -1,5 +1,15 @@ #[derive(Clone, Copy, Default, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub struct Address([u8; Self::LENGTH]); +#[cfg_attr( + feature = "serde", + derive(serde_derive::Serialize, serde_derive::Deserialize) +)] +pub struct Address( + #[cfg_attr( + feature = "serde", + serde(with = "::serde_with::As::<::serde_with::IfIsHumanReadable>") + )] + [u8; Self::LENGTH], +); impl Address { pub const LENGTH: usize = 32; @@ -21,7 +31,7 @@ impl Address { } /// Return the underlying byte array of a Address. - pub fn to_inner(self) -> [u8; Self::LENGTH] { + pub fn into_inner(self) -> [u8; Self::LENGTH] { self.0 } @@ -104,42 +114,29 @@ impl std::fmt::Debug for Address { #[cfg(feature = "serde")] #[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] -impl serde::Serialize for Address { - fn serialize(&self, serializer: S) -> Result +struct ReadableAddress; + +#[cfg(feature = "serde")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] +impl serde_with::SerializeAs<[u8; Address::LENGTH]> for ReadableAddress { + fn serialize_as(source: &[u8; Address::LENGTH], serializer: S) -> Result where S: serde::Serializer, { - if serializer.is_human_readable() { - self.to_hex().serialize(serializer) - } else { - serializer.serialize_newtype_struct("Address", &self.0) - } + let address = Address::new(*source); + serde_with::DisplayFromStr::serialize_as(&address, serializer) } } #[cfg(feature = "serde")] #[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] -impl<'de> serde::Deserialize<'de> for Address { - fn deserialize(deserializer: D) -> Result +impl<'de> serde_with::DeserializeAs<'de, [u8; Address::LENGTH]> for ReadableAddress { + fn deserialize_as(deserializer: D) -> Result<[u8; Address::LENGTH], D::Error> where D: serde::Deserializer<'de>, { - use std::str::FromStr; - - if deserializer.is_human_readable() { - let s = ::deserialize(deserializer)?; - Address::from_str(&s).map_err(serde::de::Error::custom) - } else { - // In order to preserve the Serde data model and help analysis tools, - // make sure to wrap our value in a container with the same name - // as the original type. - #[derive(::serde_derive::Deserialize)] - #[serde(rename = "Address")] - struct Value([u8; Address::LENGTH]); - - let value = Value::deserialize(deserializer)?; - Ok(Address::new(value.0)) - } + let address: Address = serde_with::DisplayFromStr::deserialize_as(deserializer)?; + Ok(address.into_inner()) } } @@ -169,4 +166,14 @@ mod test { assert_eq!(actual.to_string(), expected); } + + #[test] + fn formats() { + let actual = Address::from_hex("0x2").unwrap(); + + println!("{}", serde_json::to_string(&actual).unwrap()); + println!("{:?}", bcs::to_bytes(&actual).unwrap()); + let a: Address = serde_json::from_str("\"0x2\"").unwrap(); + println!("{a}"); + } }