diff --git a/src/key.rs b/src/key.rs index 8dab98f..44d7fa7 100644 --- a/src/key.rs +++ b/src/key.rs @@ -187,30 +187,7 @@ impl SecretKey { } } -#[cfg(feature = "serde")] -impl ::serde::Serialize for SecretKey { - fn serialize(&self, s: S) -> Result { - s.serialize_bytes(&self.0) - } -} - -#[cfg(feature = "serde")] -impl<'de> ::serde::Deserialize<'de> for SecretKey { - fn deserialize>(d: D) -> Result { - use ::serde::de::Error; - - // serde can actually deserialize a 32-byte array directly rather than deserializing - // a byte slice and copying, but it has special code for byte-slices and no special - // code for byte-arrays, meaning this is actually simpler and more efficient - let mut arr = [0; 32]; - let sl: &[u8] = ::serde::Deserialize::deserialize(d)?; - if sl.len() != constants::SECRET_KEY_SIZE { - return Err(D::Error::invalid_length(sl.len(), &"32")); - } - arr.copy_from_slice(sl); - Ok(SecretKey(arr)) - } -} +serde_impl!(SecretKey, constants::SECRET_KEY_SIZE); impl PublicKey { /// Obtains a raw const pointer suitable for use with FFI functions @@ -739,13 +716,16 @@ mod test { #[cfg(feature = "serde")] #[test] fn test_signature_serde() { - use serde_test::{Token, assert_tokens}; + use serde_test::{Configure, Token, assert_tokens}; static SK_BYTES: [u8; 32] = [ 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 2, 3, 4, 5, 6, 7, 0xff, 0xff, 0, 0, 0xff, 0xff, 0, 0, 99, 99, 99, 99, 99, 99, 99, 99 ]; + static SK_STR: &'static str = "\ + 01010101010101010001020304050607ffff0000ffff00006363636363636363\ + "; static PK_BYTES: [u8; 33] = [ 0x02, 0x18, 0x84, 0x57, 0x81, 0xf6, 0x31, 0xc4, 0x8f, @@ -759,7 +739,8 @@ mod test { let sk = SecretKey::from_slice(&SK_BYTES).unwrap(); let pk = PublicKey::from_secret_key(&s, &sk); - assert_tokens(&sk, &[Token::BorrowedBytes(&SK_BYTES[..])]); + assert_tokens(&sk.compact(), &[Token::BorrowedBytes(&SK_BYTES[..])]); + assert_tokens(&sk.readable(), &[Token::BorrowedStr(SK_STR)]); assert_tokens(&pk, &[Token::BorrowedBytes(&PK_BYTES[..])]); } } diff --git a/src/macros.rs b/src/macros.rs index 90f9ee3..c36dc8b 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -158,3 +158,47 @@ macro_rules! impl_raw_debug { } } } + +#[cfg(feature="serde")] +/// Implements `Serialize` and `Deserialize` for a type `$t` which represents +/// a newtype over a byte-slice over length `$len`. Type `$t` must implement +/// the `FromStr` and `Display` trait. +macro_rules! serde_impl( + ($t:ident, $len:expr) => ( + impl ::serde::Serialize for $t { + fn serialize(&self, s: S) -> Result { + if s.is_human_readable() { + s.collect_str(self) + } else { + s.serialize_bytes(&self[..]) + } + } + } + + impl<'de> ::serde::Deserialize<'de> for $t { + fn deserialize>(d: D) -> Result<$t, D::Error> { + use ::serde::de::Error; + use core::str::FromStr; + + if d.is_human_readable() { + let sl: &str = ::serde::Deserialize::deserialize(d)?; + SecretKey::from_str(sl).map_err(D::Error::custom) + } else { + let sl: &[u8] = ::serde::Deserialize::deserialize(d)?; + if sl.len() != $len { + Err(D::Error::invalid_length(sl.len(), &stringify!($len))) + } else { + let mut ret = [0; $len]; + ret.copy_from_slice(sl); + Ok($t(ret)) + } + } + } + } + ) +); + +#[cfg(not(feature="serde"))] +macro_rules! serde_impl( + ($t:ident, $len:expr) => () +);