From 064207edab2372a10514f6dc3c34699db022400f Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Fri, 25 Oct 2024 14:40:27 +0200 Subject: [PATCH] feat: add serde feature --- Cargo.toml | 6 ++++++ src/lib.rs | 3 +++ src/serde.rs | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+) create mode 100644 src/serde.rs diff --git a/Cargo.toml b/Cargo.toml index 9101185..7279f56 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,6 +38,9 @@ std = [] # "signature" crate.) traits-preview = ["digest"] +# Adds serialization and deserialization abilities to `Hash` +serde = ["dep:serde"] + # ---------- Features below this line are undocumented and unstable. ---------- # The following features are mainly intended for testing and benchmarking, and # they might change or disappear at any time without a major version bump. @@ -91,6 +94,7 @@ rayon = { version = "1.2.1", optional = true } cfg-if = "1.0.0" digest = { version = "0.10.1", features = [ "mac" ], optional = true } zeroize_crate = { package = "zeroize", version = "1", default-features = false, features = ["zeroize_derive"], optional = true } +serde = { version = "1.0.213", optional = true } [dev-dependencies] hex = "0.4.2" @@ -99,6 +103,8 @@ rand = "0.8.0" rand_chacha = "0.3.0" reference_impl = { path = "./reference_impl" } hmac = "0.12.0" +postcard = { version = "1.0.10", features = ["use-std"] } +serde_json = "1.0.132" [build-dependencies] cc = "1.0.4" diff --git a/src/lib.rs b/src/lib.rs index 5f25ec2..0a69c88 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -73,6 +73,9 @@ extern crate zeroize_crate as zeroize; // Needed because `zeroize::Zeroize` assu #[cfg(test)] mod test; +#[cfg(feature = "serde")] +mod serde; + // The guts module is for incremental use cases like the `bao` crate that need // to explicitly compute chunk and parent chaining values. It is semi-stable // and likely to keep working, but largely undocumented and not intended for diff --git a/src/serde.rs b/src/serde.rs new file mode 100644 index 0000000..561f009 --- /dev/null +++ b/src/serde.rs @@ -0,0 +1,58 @@ +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; + +use super::Hash; + +impl Serialize for Hash { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + if serializer.is_human_readable() { + serializer.serialize_str(self.to_string().as_str()) + } else { + self.as_bytes().serialize(serializer) + } + } +} + +impl<'de> Deserialize<'de> for Hash { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + if deserializer.is_human_readable() { + let s = String::deserialize(deserializer)?; + s.parse().map_err(de::Error::custom) + } else { + let data: [u8; 32] = Deserialize::deserialize(deserializer)?; + Ok(Hash::from(data)) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use crate::hash; + + #[test] + fn test_hash_postcard() { + let hash = hash(b"hello"); + let ser = postcard::to_stdvec(&hash).unwrap(); + let de: Hash = postcard::from_bytes(&ser).unwrap(); + assert_eq!(hash, de); + + assert_eq!(ser.len(), 32); + } + + #[test] + fn test_hash_json() { + let hash = hash(b"hello"); + let ser = serde_json::to_string(&hash).unwrap(); + let de: Hash = serde_json::from_str(&ser).unwrap(); + assert_eq!(hash, de); + // 64 bytes of hex + 2 quotes + assert_eq!(ser.len(), 66); + } +}