From a3ae5c88c5051d0c8f99f041f1fc1d0734a0f24b Mon Sep 17 00:00:00 2001 From: amanusk Date: Wed, 14 Aug 2019 11:35:57 +0300 Subject: [PATCH] Fix abci::Code parser to accept a string or an int Addresses issue #10 Added tests for both cases Signed-off-by: amanusk --- src/abci/code.rs | 50 +++++++++++++++----- tests/rpc.rs | 18 +++++++ tests/support/rpc/broadcast_tx_sync_int.json | 10 ++++ 3 files changed, 67 insertions(+), 11 deletions(-) create mode 100644 tests/support/rpc/broadcast_tx_sync_int.json diff --git a/src/abci/code.rs b/src/abci/code.rs index 45d6b37d0..f14836457 100644 --- a/src/abci/code.rs +++ b/src/abci/code.rs @@ -1,4 +1,6 @@ -use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer}; +use serde::de::{Deserialize, Deserializer, Visitor}; +use serde::{Serialize, Serializer}; +use std::fmt; /// ABCI application response codes. /// @@ -54,18 +56,44 @@ impl From for u32 { } } -impl<'de> Deserialize<'de> for Code { - fn deserialize>(deserializer: D) -> Result { - Ok(Code::from( - String::deserialize(deserializer)? - .parse::() - .map_err(|e| D::Error::custom(format!("{}", e)))?, - )) - } -} - impl Serialize for Code { fn serialize(&self, serializer: S) -> Result { self.value().serialize(serializer) } } + +impl<'de> Deserialize<'de> for Code { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct CodeVisitor; + + impl<'de> Visitor<'de> for CodeVisitor { + type Value = Code; + + fn expecting(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.write_str("integer or string") + } + + fn visit_u64(self, val: u64) -> Result + where + E: serde::de::Error, + { + Ok(Code::from(val as u32)) + } + + fn visit_str(self, val: &str) -> Result + where + E: serde::de::Error, + { + match val.parse::() { + Ok(val) => self.visit_u32(val), + Err(_) => Err(E::custom("failed to parse integer")), + } + } + } + + deserializer.deserialize_any(CodeVisitor) + } +} diff --git a/tests/rpc.rs b/tests/rpc.rs index 994c456e9..c1b5e7204 100644 --- a/tests/rpc.rs +++ b/tests/rpc.rs @@ -3,6 +3,7 @@ #[cfg(feature = "rpc")] mod endpoints { use std::{fs, path::PathBuf}; + use tendermint::abci::Code; use tendermint::rpc::{self, endpoint, Response}; const EXAMPLE_APP: &str = "GaiaApp"; @@ -122,6 +123,23 @@ mod endpoints { )) .unwrap(); + assert_eq!(response.code, Code::Ok); + + assert_eq!( + &response.hash.to_string(), + "0D33F2F03A5234F38706E43004489E061AC40A2E" + ); + } + + #[test] + fn broadcast_tx_sync_int() { + let response = endpoint::broadcast::tx_sync::Response::from_json(&read_json_fixture( + "broadcast_tx_sync_int", + )) + .unwrap(); + + assert_eq!(response.code, Code::Ok); + assert_eq!( &response.hash.to_string(), "0D33F2F03A5234F38706E43004489E061AC40A2E" diff --git a/tests/support/rpc/broadcast_tx_sync_int.json b/tests/support/rpc/broadcast_tx_sync_int.json new file mode 100644 index 000000000..10e1a2283 --- /dev/null +++ b/tests/support/rpc/broadcast_tx_sync_int.json @@ -0,0 +1,10 @@ +{ + "jsonrpc": "2.0", + "id": "", + "result": { + "code": 0, + "data": "", + "log": "", + "hash": "0D33F2F03A5234F38706E43004489E061AC40A2E" + } +}