diff --git a/.travis.yml b/.travis.yml index 15a3f1da..f92e1a7f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,4 +14,8 @@ script: - cargo test --verbose - cargo build --verbose --features=bitcoinconsensus - cargo test --verbose --features=bitcoinconsensus + - cargo build --verbose --features=serde + - cargo test --verbose --features=serde + - cargo build --verbose --features=serde-decimal + - cargo test --verbose --features=serde-decimal - if [ "$(rustup show | grep default | grep stable)" != "" ]; then cd fuzz && cargo test --verbose && ./travis-fuzz.sh; fi diff --git a/Cargo.toml b/Cargo.toml index 7b0ea38c..7902134f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,16 +17,24 @@ path = "src/lib.rs" [features] fuzztarget = ["secp256k1/fuzztarget"] +serde-decimal = ["serde", "strason"] [dependencies] bitcoin-bech32 = "0.8.0" byteorder = "1.1" rand = "0.3" rust-crypto = "0.2" -serde = "0.6" -strason = "0.3" bitcoinconsensus = { version = "0.16", optional = true } +[dependencies.serde] +version = "1" +optional = true + +[dependencies.strason] +version = "0.4" +optional = true +default-features = false + [dependencies.hex] git = "https://github.com/KokaKiwi/rust-hex" rev = "19fd37137686c30058bd9d11d21590e726ffdf31" diff --git a/src/blockdata/opcodes.rs b/src/blockdata/opcodes.rs index 037577a4..703de250 100644 --- a/src/blockdata/opcodes.rs +++ b/src/blockdata/opcodes.rs @@ -20,7 +20,7 @@ #![allow(non_camel_case_types)] -use serde; +#[cfg(feature = "serde")] use serde; // Heavy stick to translate between opcode types use std::mem::transmute; @@ -620,11 +620,13 @@ impl ConsensusEncodable for All { } } +#[cfg(feature = "serde")] impl serde::Serialize for All { - fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> - where S: serde::Serializer, + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, { - serializer.visit_str(&self.to_string()) + serializer.serialize_str(&self.to_string()) } } @@ -656,11 +658,13 @@ pub enum Class { display_from_debug!(Class); +#[cfg(feature = "serde")] impl serde::Serialize for Class { - fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> - where S: serde::Serializer, + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, { - serializer.visit_str(&self.to_string()) + serializer.serialize_str(&self.to_string()) } } diff --git a/src/blockdata/script.rs b/src/blockdata/script.rs index 7e8b8674..4a24691e 100644 --- a/src/blockdata/script.rs +++ b/src/blockdata/script.rs @@ -28,7 +28,7 @@ use std::default::Default; use std::{error, fmt}; use crypto::digest::Digest; -use serde; +#[cfg(feature = "serde")] use serde; use blockdata::opcodes; use network::encodable::{ConsensusDecodable, ConsensusEncodable}; @@ -561,39 +561,57 @@ impl From> for Builder { impl_index_newtype!(Builder, u8); -// User-facing serialization -impl serde::Serialize for Script { - fn serialize(&self, s: &mut S) -> Result<(), S::Error> - where S: serde::Serializer, +#[cfg(feature = "serde")] +impl<'de> serde::Deserialize<'de> for Script { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, { - s.visit_str(&format!("{:x}", self)) - } -} + use std::fmt::{self, Formatter}; -impl serde::Deserialize for Script { - fn deserialize(d: &mut D) -> Result - where D: serde::Deserializer - { - struct ScriptVisitor; - impl serde::de::Visitor for ScriptVisitor { + struct Visitor; + impl<'de> serde::de::Visitor<'de> for Visitor { type Value = Script; - fn visit_string(&mut self, v: String) -> Result - where E: serde::de::Error + fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { + formatter.write_str("a script") + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + let v: Vec = ::hex::decode(v).map_err(E::custom)?; + Ok(Script::from(v)) + } + + fn visit_borrowed_str(self, v: &'de str) -> Result + where + E: serde::de::Error, + { + self.visit_str(v) + } + + fn visit_string(self, v: String) -> Result + where + E: serde::de::Error, { self.visit_str(&v) } - - fn visit_str(&mut self, hex_str: &str) -> Result - where E: serde::de::Error - { - let raw_vec: Vec = ::hex::decode(hex_str) - .map_err(|_| serde::de::Error::syntax("bad script hex"))?; - Ok(Script::from(raw_vec)) - } } - d.visit(ScriptVisitor) + deserializer.deserialize_str(Visitor) + } +} + +#[cfg(feature = "serde")] +impl serde::Serialize for Script { + /// User-facing serialization for `Script`. + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&format!("{:x}", self)) } } @@ -704,11 +722,12 @@ mod test { } #[test] + #[cfg(all(feature = "serde", feature = "strason"))] fn script_json_serialize() { - use strason; + use strason::Json; let original = hex_script!("827651a0698faaa9a8a7a687"); - let json = strason::from_serialize(&original).unwrap(); + let json = Json::from_serialize(&original).unwrap(); assert_eq!(json.to_bytes(), b"\"827651a0698faaa9a8a7a687\""); assert_eq!(json.string(), Some("827651a0698faaa9a8a7a687")); let des = json.into_deserialize().unwrap(); diff --git a/src/blockdata/transaction.rs b/src/blockdata/transaction.rs index f5d62608..a597f948 100644 --- a/src/blockdata/transaction.rs +++ b/src/blockdata/transaction.rs @@ -27,7 +27,6 @@ use byteorder::{LittleEndian, WriteBytesExt}; use std::default::Default; use std::fmt; #[cfg(feature="bitcoinconsensus")] use std::collections::HashMap; -use serde; use util::hash::Sha256dHash; #[cfg(feature="bitcoinconsensus")] use blockdata::script; @@ -442,13 +441,16 @@ impl SigHashType { #[cfg(test)] mod tests { - use strason; + #[cfg(all(feature = "serde", feature = "strason"))] + use strason::Json; use super::{Transaction, TxIn}; use blockdata::script::Script; use network::serialize::BitcoinHash; - use network::serialize::{serialize, deserialize}; + #[cfg(all(feature = "serde", feature = "strason"))] + use network::serialize::serialize; + use network::serialize::deserialize; use util::hash::Sha256dHash; use util::misc::hex_bytes; @@ -563,11 +565,12 @@ mod tests { } #[test] + #[cfg(all(feature = "serde", feature = "strason"))] fn test_txn_encode_decode() { let hex_tx = hex_bytes("0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000").unwrap(); let tx: Transaction = deserialize(&hex_tx).unwrap(); - let encoded = strason::from_serialize(&tx).unwrap(); + let encoded = Json::from_serialize(&tx).unwrap(); let decoded = encoded.into_deserialize().unwrap(); assert_eq!(tx, decoded); } @@ -586,12 +589,13 @@ mod tests { // Test decoding transaction `4be105f158ea44aec57bf12c5817d073a712ab131df6f37786872cfc70734188` // from testnet, which is the first BIP144-encoded transaction I encountered. #[test] + #[cfg(all(feature = "serde", feature = "strason"))] fn test_segwit_tx_decode() { let hex_tx = hex_bytes("010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff3603da1b0e00045503bd5704c7dd8a0d0ced13bb5785010800000000000a636b706f6f6c122f4e696e6a61506f6f6c2f5345475749542fffffffff02b4e5a212000000001976a914876fbb82ec05caa6af7a3b5e5a983aae6c6cc6d688ac0000000000000000266a24aa21a9edf91c46b49eb8a29089980f02ee6b57e7d63d33b18b4fddac2bcd7db2a39837040120000000000000000000000000000000000000000000000000000000000000000000000000").unwrap(); let tx: Transaction = deserialize(&hex_tx).unwrap(); assert_eq!(tx.get_weight(), 780); - let encoded = strason::from_serialize(&tx).unwrap(); + let encoded = Json::from_serialize(&tx).unwrap(); let decoded = encoded.into_deserialize().unwrap(); assert_eq!(tx, decoded); diff --git a/src/internal_macros.rs b/src/internal_macros.rs index ac4b281c..a4c5e00e 100644 --- a/src/internal_macros.rs +++ b/src/internal_macros.rs @@ -173,47 +173,50 @@ macro_rules! impl_array_newtype { macro_rules! impl_array_newtype_encodable { ($thing:ident, $ty:ty, $len:expr) => { - - impl ::serde::Deserialize for $thing { - fn deserialize(d: &mut D) -> Result<$thing, D::Error> - where D: ::serde::Deserializer + #[cfg(feature = "serde")] + impl<'de> $crate::serde::Deserialize<'de> for $thing { + fn deserialize(deserializer: D) -> Result + where + D: $crate::serde::Deserializer<'de>, { - // We have to define the Visitor struct inside the function - // to make it local ... what we really need is that it's - // local to the macro, but this is Close Enough. - struct Visitor { - marker: ::std::marker::PhantomData<$thing>, - } - impl ::serde::de::Visitor for Visitor { + use $crate::std::fmt::{self, Formatter}; + + struct Visitor; + impl<'de> $crate::serde::de::Visitor<'de> for Visitor { type Value = $thing; + fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { + formatter.write_str("a fixed size array") + } + #[inline] - fn visit_seq(&mut self, mut v: V) -> Result<$thing, V::Error> - where V: ::serde::de::SeqVisitor + fn visit_seq(self, mut seq: A) -> Result + where + A: $crate::serde::de::SeqAccess<'de>, { let mut ret: [$ty; $len] = [0; $len]; for item in ret.iter_mut() { - *item = match v.visit()? { + *item = match seq.next_element()? { Some(c) => c, - None => return Err(::serde::de::Error::end_of_stream()) + None => return Err($crate::serde::de::Error::custom("end of stream")) }; } - v.end()?; Ok($thing(ret)) } } - // Begin actual function - d.visit(Visitor { marker: ::std::marker::PhantomData }) + deserializer.deserialize_seq(Visitor) } } - impl ::serde::Serialize for $thing { - fn serialize(&self, s: &mut S) -> Result<(), S::Error> - where S: ::serde::Serializer + #[cfg(feature = "serde")] + impl $crate::serde::Serialize for $thing { + fn serialize(&self, serializer: S) -> Result + where + S: $crate::serde::Serializer, { let &$thing(ref dat) = self; - (&dat[..]).serialize(s) + (&dat[..]).serialize(serializer) } } } @@ -286,303 +289,122 @@ macro_rules! hex_script (($s:expr) => (::blockdata::script::Script::from(::hex:: #[cfg(test)] macro_rules! hex_hash (($s:expr) => (::util::hash::Sha256dHash::from(&::hex::decode($s).unwrap()[..]))); - -// Macros to replace serde's codegen while that is not stable -// Taken from rust-jsonrpc 8a50735712cb7870990314cc150ab9c2955dbfd5 - -#[macro_export] -macro_rules! __rust_jsonrpc_internal__define_anything_type { - () => ( - struct Anything; - struct AnythingVisitor; - impl ::serde::de::Visitor for AnythingVisitor { - type Value = Anything; - - fn visit_bool(&mut self, _: bool) -> Result { Ok(Anything) } - fn visit_i64(&mut self, _: i64) -> Result { Ok(Anything) } - fn visit_u64(&mut self, _: u64) -> Result { Ok(Anything) } - fn visit_f64(&mut self, _: f64) -> Result { Ok(Anything) } - fn visit_str(&mut self, _: &str) -> Result { Ok(Anything) } - fn visit_string(&mut self, _: String) -> Result { Ok(Anything) } - fn visit_unit(&mut self) -> Result { Ok(Anything) } - fn visit_none(&mut self) -> Result { Ok(Anything) } - - fn visit_some(&mut self, d: &mut D) -> Result { - serde::de::Deserialize::deserialize(d) - } - - fn visit_seq(&mut self, v: V) -> Result { - let _: Vec = ::serde::de::impls::VecVisitor::new().visit_seq(v)?; - Ok(Anything) - } - - fn visit_map(&mut self, mut v: V) -> Result { - while let Some((Anything, Anything)) = v.visit()? { } - v.end()?; - Ok(Anything) - } - } - - impl ::serde::Deserialize for Anything { - fn deserialize(deserializer: &mut D) -> Result - where D: ::serde::de::Deserializer - { - deserializer.visit(AnythingVisitor) - } - } - ) -} - -#[macro_export] macro_rules! serde_struct_impl { - ($name:ident, $($fe:ident $(<- $alt:expr)*),*) => ( - impl ::serde::Deserialize for $name { - fn deserialize(deserializer: &mut D) -> Result<$name, D::Error> - where D: serde::de::Deserializer + ($name:ident, $($fe:ident),*) => ( + #[cfg(feature = "serde")] + impl<'de> $crate::serde::Deserialize<'de> for $name { + fn deserialize(deserializer: D) -> Result<$name, D::Error> + where + D: $crate::serde::de::Deserializer<'de>, { - // begin type defs - __rust_jsonrpc_internal__define_anything_type!(); + use $crate::std::fmt::{self, Formatter}; + use $crate::serde::de::IgnoredAny; #[allow(non_camel_case_types)] enum Enum { Unknown__Field, $($fe),* } struct EnumVisitor; - impl ::serde::de::Visitor for EnumVisitor { + impl<'de> $crate::serde::de::Visitor<'de> for EnumVisitor { type Value = Enum; - fn visit_str(&mut self, value: &str) -> Result - where E: ::serde::de::Error + fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { + formatter.write_str("a field name") + } + + fn visit_str(self, v: &str) -> Result + where + E: $crate::serde::de::Error, { - match value { + match v { $( stringify!($fe) => Ok(Enum::$fe) - $(, $alt => Ok(Enum::$fe))* ),*, _ => Ok(Enum::Unknown__Field) } } } - impl ::serde::Deserialize for Enum { - fn deserialize(deserializer: &mut D) -> Result - where D: ::serde::de::Deserializer + impl<'de> $crate::serde::Deserialize<'de> for Enum { + fn deserialize(deserializer: D) -> Result + where + D: ::serde::de::Deserializer<'de>, { - deserializer.visit_str(EnumVisitor) + deserializer.deserialize_str(EnumVisitor) } } struct Visitor; - impl ::serde::de::Visitor for Visitor { + impl<'de> $crate::serde::de::Visitor<'de> for Visitor { type Value = $name; - fn visit_map(&mut self, mut v: V) -> Result<$name, V::Error> - where V: ::serde::de::MapVisitor + fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { + formatter.write_str("a struct") + } + + fn visit_map(self, mut map: A) -> Result + where + A: $crate::serde::de::MapAccess<'de>, { + use $crate::serde::de::Error; + $(let mut $fe = None;)* loop { - match v.visit_key()? { - Some(Enum::Unknown__Field) => { let _: Anything = v.visit_value()?; } - $(Some(Enum::$fe) => { $fe = Some(v.visit_value()?); })* + match map.next_key::()? { + Some(Enum::Unknown__Field) => { + map.next_value::()?; + } + $( + Some(Enum::$fe) => { + $fe = Some(map.next_value()?); + } + )* None => { break; } } } - $(let $fe = match $fe { - Some(x) => x, - None => v.missing_field(stringify!($fe))?, - };)* - v.end()?; - Ok($name{ $($fe: $fe),* }) + $( + let $fe = match $fe { + Some(x) => x, + None => return Err(A::Error::missing_field(stringify!($fe))), + }; + )* + + let ret = $name { + $($fe: $fe),* + }; + + Ok(ret) } } // end type defs static FIELDS: &'static [&'static str] = &[$(stringify!($fe)),*]; - deserializer.visit_struct(stringify!($name), FIELDS, Visitor) + deserializer.deserialize_struct(stringify!($name), FIELDS, Visitor) } } - impl ::serde::Serialize for $name { - fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> - where S: ::serde::Serializer + #[cfg(feature = "serde")] + impl<'de> $crate::serde::Serialize for $name { + fn serialize(&self, serializer: S) -> Result + where + S: $crate::serde::Serializer, { - // begin type defs - #[repr(u16)] - #[derive(Copy, Clone)] - #[allow(non_camel_case_types)] - #[allow(dead_code)] - enum State { $($fe),* , Finished } + use $crate::serde::ser::SerializeStruct; - struct MapVisitor<'a> { - value: &'a $name, - state: State, - } + // Only used to get the struct length. + static FIELDS: &'static [&'static str] = &[$(stringify!($fe)),*]; - impl<'a> ::serde::ser::MapVisitor for MapVisitor<'a> { - fn visit(&mut self, serializer: &mut S) -> Result, S::Error> - where S: ::serde::Serializer - { - match self.state { - $(State::$fe => { - self.state = unsafe { ::std::mem::transmute(self.state as u16 + 1) }; - // Use the last alternate name for serialization; in the common case - // with zero or one alternates this does the RIght Thing - let names = [stringify!($fe), $($alt),*]; - Ok(Some(serializer.visit_struct_elt(names[names.len() - 1], &self.value.$fe)?)) - })* - State::Finished => { - Ok(None) - } - } - } - } - // end type defs + let mut st = serializer.serialize_struct(stringify!($name), FIELDS.len())?; - serializer.visit_struct(stringify!($name), MapVisitor { - value: self, - state: unsafe { ::std::mem::transmute(0u16) }, - }) + $( + st.serialize_field(stringify!($fe), &self.$fe)?; + )* + + st.end() } } ) } - -#[macro_export] -macro_rules! serde_struct_enum_impl { - ($name:ident, - $($varname:ident, $structname:ident, $($fe:ident $(<- $alt:expr)*),*);* - ) => ( - impl ::serde::Deserialize for $name { - fn deserialize(deserializer: &mut D) -> Result<$name, D::Error> - where D: serde::de::Deserializer - { - // start type defs - __rust_jsonrpc_internal__define_anything_type!(); - - $(#[allow(non_camel_case_types)] enum $varname { $($fe),* })* - #[allow(non_camel_case_types)] - enum Enum { Unknown__Field, $($varname($varname)),* } - - struct EnumVisitor; - impl ::serde::de::Visitor for EnumVisitor { - type Value = Enum; - - fn visit_str(&mut self, value: &str) -> Result - where E: ::serde::de::Error - { - $($( - if value == stringify!($fe) $(|| value == $alt)* { - Ok(Enum::$varname($varname::$fe)) - } else)*)* { - Ok(Enum::Unknown__Field) - } - } - } - - impl ::serde::Deserialize for Enum { - fn deserialize(deserializer: &mut D) -> Result - where D: ::serde::de::Deserializer - { - deserializer.visit_str(EnumVisitor) - } - } - - struct Visitor; - - impl ::serde::de::Visitor for Visitor { - type Value = $name; - - #[allow(non_snake_case)] //for $structname - #[allow(unused_assignments)] // for `$fe = None` hack - fn visit_map(&mut self, mut v: V) -> Result<$name, V::Error> - where V: ::serde::de::MapVisitor - { - $( - $(let mut $fe = None;)* - // In case of multiple variants having the same field, some of - // the above lets will get shadowed. We therefore need to tell - // rustc its type, since it otherwise cannot infer it, causing - // a compilation error. Hence this hack, which the denizens of - // #rust and I had a good laugh over: - if false { let _ = $structname { $($fe: $fe.unwrap()),* }; } - // The above expression moved $fe so we have to reassign it :) - $($fe = None;)* - )* - - loop { - match v.visit_key()? { - Some(Enum::Unknown__Field) => { let _: Anything = v.visit_value()?; } - $($(Some(Enum::$varname($varname::$fe)) => { - $fe = Some(v.visit_value()?); })*)* - None => { break; } - } - } - - // try to find a variant for which we have all fields - $( - let mut $structname = true; - $(if $fe.is_none() { $structname = false })* - // if we found one, success. extra fields is not an error, - // it'd be too much of a PITA to manage overlapping field - // sets otherwise. - if $structname { - $(let $fe = $fe.unwrap();)* - v.end()?; - return Ok($name::$varname($structname { $($fe: $fe),* })) - } - )* - // If we get here we failed - Err(::serde::de::Error::syntax("did not get all fields")) - } - } - // end type defs - - static FIELDS: &'static [&'static str] = &[$($(stringify!($fe)),*),*]; - - deserializer.visit_struct(stringify!($name), FIELDS, Visitor) - } - } - - // impl Serialize (and Deserialize, tho we don't need it) for the underlying structs - $( serde_struct_impl!($structname, $($fe $(<- $alt)*),*); )* - // call serialize on the right one - impl ::serde::Serialize for $name { - fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> - where S: ::serde::Serializer - { - match *self { - $($name::$varname(ref x) => x.serialize(serializer)),* - } - } - } - ) -} - -#[cfg(test)] -mod tests { - use serde; - - pub struct Variant1 { - success: bool, - success_message: String - } - - pub struct Variant2 { - success: bool, - errors: Vec - } - - pub enum Reply { - Good(Variant1), - Bad(Variant2), - } - serde_struct_enum_impl!(Reply, - Good, Variant1, success, success_message; - Bad, Variant2, success, errors - ); -} - - diff --git a/src/lib.rs b/src/lib.rs index 59eb79de..c84b7683 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,8 +47,8 @@ extern crate crypto; extern crate hex; extern crate rand; extern crate secp256k1; -extern crate serde; -extern crate strason; +#[cfg(feature = "serde")] extern crate serde; +#[cfg(feature = "strason")] extern crate strason; #[cfg(all(test, feature = "unstable"))] extern crate test; #[cfg(feature="bitcoinconsensus")] extern crate bitcoinconsensus; @@ -62,4 +62,3 @@ pub mod macros; pub mod network; pub mod blockdata; pub mod util; - diff --git a/src/macros.rs b/src/macros.rs index 598de508..1f00615e 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -83,7 +83,13 @@ macro_rules! nu_select { #[macro_export] macro_rules! user_enum { - ($(#[$attr:meta])* pub enum $name:ident { $(#[$doc:meta] $elem:ident <-> $txt:expr),* }) => ( + ( + $(#[$attr:meta])* + pub enum $name:ident { + $(#[$doc:meta] + $elem:ident <-> $txt:expr),* + } + ) => ( $(#[$attr])* pub enum $name { $(#[$doc] $elem),* @@ -119,38 +125,62 @@ macro_rules! user_enum { } } - impl ::serde::Deserialize for $name { + #[cfg(feature = "serde")] + impl<'de> $crate::serde::Deserialize<'de> for $name { #[inline] - fn deserialize(d: &mut D) -> Result<$name, D::Error> - where D: ::serde::Deserializer + fn deserialize(deserializer: D) -> Result + where + D: $crate::serde::Deserializer<'de>, { + use $crate::std::fmt::{self, Formatter}; + struct Visitor; - impl ::serde::de::Visitor for Visitor { + impl<'de> $crate::serde::de::Visitor<'de> for Visitor { type Value = $name; - fn visit_string(&mut self, v: String) -> Result<$name, E> - where E: ::serde::de::Error + fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { + formatter.write_str("an enum value") + } + + fn visit_str(self, v: &str) -> Result + where + E: $crate::serde::de::Error, + { + static FIELDS: &'static [&'static str] = &[$(stringify!($txt)),*]; + + $( if v == $txt { Ok($name::$elem) } )else* + else { + Err(E::unknown_variant(v, FIELDS)) + } + } + + fn visit_borrowed_str(self, v: &'de str) -> Result + where + E: $crate::serde::de::Error, + { + self.visit_str(v) + } + + fn visit_string(self, v: String) -> Result + where + E: $crate::serde::de::Error, { self.visit_str(&v) } - fn visit_str(&mut self, s: &str) -> Result<$name, E> - where E: ::serde::de::Error - { - $( if s == $txt { Ok($name::$elem) } )else* - else { Err(::serde::de::Error::syntax(stringify!($name))) } - } } - d.visit(Visitor) + deserializer.deserialize_str(Visitor) } } + #[cfg(feature = "serde")] impl ::serde::Serialize for $name { - fn serialize(&self, s: &mut S) -> Result<(), S::Error> - where S: ::serde::Serializer + fn serialize(&self, serializer: S) -> Result + where + S: ::serde::Serializer, { - s.visit_str(&self.to_string()) + serializer.serialize_str(&self.to_string()) } } ); diff --git a/src/test_macros.rs b/src/test_macros.rs index 9a711ce3..b2e9cafc 100644 --- a/src/test_macros.rs +++ b/src/test_macros.rs @@ -17,12 +17,13 @@ //! Internal macros used for unit tests #[macro_export] +#[cfg(all(feature = "serde", feature = "strason"))] macro_rules! serde_round_trip ( ($var:expr) => ({ - use strason; + use $crate::strason::Json; let start = $var; - let encoded = strason::from_serialize(&start).unwrap(); + let encoded = Json::from_serialize(&start).unwrap(); let decoded = encoded.into_deserialize().unwrap(); assert_eq!(start, decoded); }) diff --git a/src/util/bip32.rs b/src/util/bip32.rs index 3e2c9eea..200cce74 100644 --- a/src/util/bip32.rs +++ b/src/util/bip32.rs @@ -20,7 +20,7 @@ use std::default::Default; use std::io::Cursor; use std::{error, fmt}; use std::str::FromStr; -use serde::{Serialize, Deserialize, Serializer, Deserializer}; +#[cfg(feature = "serde")] use serde; use byteorder::{BigEndian, ByteOrder, ReadBytesExt}; use crypto::digest::Digest; @@ -167,17 +167,23 @@ impl fmt::Display for ChildNumber { } } -impl Serialize for ChildNumber { - fn serialize(&self, s: &mut S) -> Result<(), S::Error> { - u32::from(*self).serialize(s) +#[cfg(feature = "serde")] +impl<'de> serde::Deserialize<'de> for ChildNumber { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + u32::deserialize(deserializer).map(ChildNumber::from) } } -impl Deserialize for ChildNumber { - fn deserialize(d: &mut D) -> Result { - let n: u32 = Deserialize::deserialize(d)?; - - Ok(ChildNumber::from(n)) +#[cfg(feature = "serde")] +impl serde::Serialize for ChildNumber { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + u32::from(*self).serialize(serializer) } } @@ -673,6 +679,7 @@ mod tests { } #[test] + #[cfg(all(feature = "serde", feature = "strason"))] pub fn encode_decode_childnumber() { serde_round_trip!(ChildNumber::from_normal_idx(0)); serde_round_trip!(ChildNumber::from_normal_idx(1)); diff --git a/src/util/decimal.rs b/src/util/decimal.rs index 938a8037..9b839c6a 100644 --- a/src/util/decimal.rs +++ b/src/util/decimal.rs @@ -23,8 +23,8 @@ use std::{fmt, ops}; -use serde::{ser, de}; -use strason::Json; +#[cfg(feature = "serde-decimal")] use serde; +#[cfg(feature = "serde-decimal")] use strason::Json; /// A fixed-point decimal type #[derive(Copy, Clone, Debug, Eq, Ord)] @@ -128,20 +128,19 @@ impl Decimal { pub fn nonnegative(&self) -> bool { self.mantissa >= 0 } } -impl ser::Serialize for Decimal { - // Serialize through strason since it will not lose precision (when serializing - // to strason itself, the value will be passed through; otherwise it will be - // encoded as a string) - fn serialize(&self, s: &mut S) -> Result<(), S::Error> { - let json = Json::from_str(&self.to_string()).unwrap(); - ser::Serialize::serialize(&json, s) - } -} - -impl de::Deserialize for Decimal { - // Deserialize through strason for the same reason as in `Serialize` - fn deserialize(d: &mut D) -> Result { - let json: Json = de::Deserialize::deserialize(d)?; +#[cfg(feature = "serde-decimal")] +impl<'de> serde::Deserialize<'de> for Decimal { + /// Deserialize a `Decimal`. + /// + /// This type is deserialized through [`strason`][1] for the same reason as + /// it's explained in the `Serialize` implementation. + /// + /// [1]: https://github.com/apoelstra/strason + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let json = Json::deserialize(deserializer)?; match json.num() { Some(s) => { // We know this will be a well-formed Json number, so we can @@ -168,11 +167,28 @@ impl de::Deserialize for Decimal { exponent: exponent, }) } - None => Err(de::Error::syntax("expected decimal, got non-numeric")) + None => Err(serde::de::Error::custom("expected decimal, got non-numeric")) } } } +#[cfg(feature = "serde-decimal")] +impl serde::Serialize for Decimal { + /// Serialize a `Decimal`. + /// + /// This type is serialized through [`strason`][1] since it will not lose + /// precision (when serializing to [`strason`][1] itself, the value will be + /// passed through; otherwise it will be encoded as a string). + /// + /// [1]: https://github.com/apoelstra/strason + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let json = Json::from_str(&self.to_string()).unwrap(); + json.serialize(serializer) + } +} impl PartialEq for UDecimal { fn eq(&self, other: &UDecimal) -> bool { @@ -246,20 +262,19 @@ impl UDecimal { } } -impl ser::Serialize for UDecimal { - // Serialize through strason since it will not lose precision (when serializing - // to strason itself, the value will be passed through; otherwise it will be - // encoded as a string) - fn serialize(&self, s: &mut S) -> Result<(), S::Error> { - let json = Json::from_str(&self.to_string()).unwrap(); - ser::Serialize::serialize(&json, s) - } -} - -impl de::Deserialize for UDecimal { - // Deserialize through strason for the same reason as in `Serialize` - fn deserialize(d: &mut D) -> Result { - let json: Json = de::Deserialize::deserialize(d)?; +#[cfg(feature = "serde-decimal")] +impl<'de> serde::Deserialize<'de> for UDecimal { + /// Deserialize an `UDecimal`. + /// + /// This type is deserialized through [`strason`][1] for the same reason as + /// it's explained in the `Serialize` implementation. + /// + /// [1]: https://github.com/apoelstra/strason + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let json = Json::deserialize(deserializer)?; match json.num() { Some(s) => { // We know this will be a well-formed Json number, so we can @@ -283,16 +298,33 @@ impl de::Deserialize for UDecimal { exponent: exponent, }) } - None => Err(de::Error::syntax("expected decimal, got non-numeric")) + None => Err(serde::de::Error::custom("expected decimal, got non-numeric")) } } } - +#[cfg(feature = "serde-decimal")] +impl serde::Serialize for UDecimal { + /// Serialize an `UDecimal`. + /// + /// This type is serialized through [`strason`][1] since it will not lose + /// precision (when serializing to [`strason`][1] itself, the value will be + /// passed through; otherwise it will be encoded as a string). + /// + /// [1]: https://github.com/apoelstra/strason + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let json = Json::from_str(&self.to_string()).unwrap(); + json.serialize(serializer) + } +} #[cfg(test)] mod tests { use super::*; + #[cfg(feature = "serde-decimal")] use strason::Json; #[test] @@ -326,6 +358,7 @@ mod tests { assert_eq!(u.integer_value(8), 123456780000); } + #[cfg(feature = "serde-decimal")] macro_rules! deserialize_round_trip( ($dec:expr, $s:expr) => ({ let d = $dec; @@ -342,6 +375,7 @@ mod tests { ); #[test] + #[cfg(feature = "serde-decimal")] fn deserialize() { deserialize_round_trip!(Decimal::new(0, 0), b"0.0"); deserialize_round_trip!(UDecimal::new(0, 0), b"0.0"); @@ -412,6 +446,7 @@ mod tests { } #[test] + #[cfg(feature = "serde-decimal")] fn json_parse() { let json = Json::from_str("0.00980000").unwrap(); assert_eq!(json.to_bytes(), b"0.00980000"); @@ -434,5 +469,3 @@ mod tests { assert_eq!(dec, UDecimal::new(98000, 7)); } } - - diff --git a/src/util/hash.rs b/src/util/hash.rs index 61caacb7..0d96df9c 100644 --- a/src/util/hash.rs +++ b/src/util/hash.rs @@ -22,7 +22,7 @@ use std::error; use std::fmt; use std::io::Cursor; use std::mem; -use serde; +#[cfg(feature = "serde")] use serde; use byteorder::{LittleEndian, WriteBytesExt}; use crypto::digest::Digest; @@ -307,50 +307,73 @@ impl Sha256dHash { } } -// Note that this outputs hashes as big endian hex numbers, so this should be -// used only for user-facing stuff. Internal and network serialization is -// little-endian and should be done using the consensus `encodable::ConsensusEncodable` -// interface. -impl serde::Serialize for Sha256dHash { - fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> - where S: serde::Serializer, - { - unsafe { - use std::{char, str}; - - let mut string = [0; 64]; - for i in 0..32 { - string[2 * i] = char::from_digit((self.0[31 - i] / 0x10) as u32, 16).unwrap() as u8; - string[2 * i + 1] = char::from_digit((self.0[31 - i] & 0x0f) as u32, 16).unwrap() as u8; - } - serializer.visit_str(str::from_utf8_unchecked(&string)) - } - } -} - -impl serde::Deserialize for Sha256dHash { +#[cfg(feature = "serde")] +impl<'de> serde::Deserialize<'de> for Sha256dHash { #[inline] - fn deserialize(d: &mut D) -> Result - where D: serde::Deserializer + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, { - struct Sha256dHashVisitor; - impl serde::de::Visitor for Sha256dHashVisitor { + use std::fmt::{self, Formatter}; + + struct Visitor; + impl<'de> serde::de::Visitor<'de> for Visitor { type Value = Sha256dHash; - fn visit_string(&mut self, v: String) -> Result - where E: serde::de::Error + fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { + formatter.write_str("a SHA256d hash") + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + Sha256dHash::from_hex(v).map_err(E::custom) + } + + fn visit_borrowed_str(self, v: &'de str) -> Result + where + E: serde::de::Error, + { + self.visit_str(v) + } + + fn visit_string(self, v: String) -> Result + where + E: serde::de::Error, { self.visit_str(&v) } - - fn visit_str(&mut self, hex_str: &str) -> Result - where E: serde::de::Error - { - Sha256dHash::from_hex(hex_str).map_err(|e| serde::de::Error::syntax(&e.to_string())) - } } - d.visit(Sha256dHashVisitor) + deserializer.deserialize_str(Visitor) + } +} + +#[cfg(feature = "serde")] +impl serde::Serialize for Sha256dHash { + /// Serialize a `Sha256dHash`. + /// + /// Note that this outputs hashes as big endian hex numbers, so this should be + /// used only for user-facing stuff. Internal and network serialization is + /// little-endian and should be done using the consensus + /// [`ConsensusEncodable`][1] interface. + /// + /// [1]: ../../network/encodable/trait.ConsensusEncodable.html + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + use std::{char, str}; + + let mut string = [0; 64]; + for i in 0..32 { + string[2 * i] = char::from_digit((self.0[31 - i] / 0x10) as u32, 16).unwrap() as u8; + string[2 * i + 1] = char::from_digit((self.0[31 - i] & 0x0f) as u32, 16).unwrap() as u8; + } + + let hex_str = unsafe { str::from_utf8_unchecked(&string) }; + serializer.serialize_str(hex_str) } } @@ -456,7 +479,8 @@ impl MerkleRoot for Vec { #[cfg(test)] mod tests { - use strason; + #[cfg(all(feature = "serde", feature = "strason"))] + use strason::Json; use network::encodable::{ConsensusEncodable, VarInt}; use network::serialize::{serialize, deserialize}; @@ -539,9 +563,10 @@ mod tests { } #[test] + #[cfg(all(feature = "serde", feature = "strason"))] fn test_hash_encode_decode() { let hash = Sha256dHash::from_data(&[]); - let encoded = strason::from_serialize(&hash).unwrap(); + let encoded = Json::from_serialize(&hash).unwrap(); assert_eq!(encoded.to_bytes(), "\"56944c5d3f98413ef45cf54545538103cc9f298e0575820ad3591376e2e0f65d\"".as_bytes()); let decoded = encoded.into_deserialize().unwrap();