-
Notifications
You must be signed in to change notification settings - Fork 784
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into standard-http
- Loading branch information
Showing
24 changed files
with
482 additions
and
95 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
[package] | ||
name = "serde_hex" | ||
version = "0.2.0" | ||
authors = ["Paul Hauner <paul@paulhauner.com>"] | ||
edition = "2018" | ||
|
||
[dependencies] | ||
serde = "1.0.110" | ||
hex = "0.4.2" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
use serde::de::{self, Visitor}; | ||
use std::fmt; | ||
|
||
pub fn encode<T: AsRef<[u8]>>(data: T) -> String { | ||
let hex = hex::encode(data); | ||
let mut s = "0x".to_string(); | ||
s.push_str(hex.as_str()); | ||
s | ||
} | ||
|
||
pub struct PrefixedHexVisitor; | ||
|
||
impl<'de> Visitor<'de> for PrefixedHexVisitor { | ||
type Value = Vec<u8>; | ||
|
||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { | ||
formatter.write_str("a hex string with 0x prefix") | ||
} | ||
|
||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> | ||
where | ||
E: de::Error, | ||
{ | ||
if value.starts_with("0x") { | ||
Ok(hex::decode(&value[2..]) | ||
.map_err(|e| de::Error::custom(format!("invalid hex ({:?})", e)))?) | ||
} else { | ||
Err(de::Error::custom("missing 0x prefix")) | ||
} | ||
} | ||
} | ||
|
||
pub struct HexVisitor; | ||
|
||
impl<'de> Visitor<'de> for HexVisitor { | ||
type Value = Vec<u8>; | ||
|
||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { | ||
formatter.write_str("a hex string (irrelevant of prefix)") | ||
} | ||
|
||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> | ||
where | ||
E: de::Error, | ||
{ | ||
Ok(hex::decode(value.trim_start_matches("0x")) | ||
.map_err(|e| de::Error::custom(format!("invalid hex ({:?})", e)))?) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod test { | ||
use super::*; | ||
|
||
#[test] | ||
fn encoding() { | ||
let bytes = vec![0, 255]; | ||
let hex = encode(&bytes); | ||
assert_eq!(hex.as_str(), "0x00ff"); | ||
|
||
let bytes = vec![]; | ||
let hex = encode(&bytes); | ||
assert_eq!(hex.as_str(), "0x"); | ||
|
||
let bytes = vec![1, 2, 3]; | ||
let hex = encode(&bytes); | ||
assert_eq!(hex.as_str(), "0x010203"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,13 @@ | ||
[package] | ||
name = "serde_utils" | ||
version = "0.2.0" | ||
authors = ["Paul Hauner <paul@paulhauner.com>"] | ||
version = "0.1.0" | ||
authors = ["Paul Hauner <paul@paulhauner.com", "Michael Sproul <michael@sigmaprime.io>"] | ||
edition = "2018" | ||
|
||
[dependencies] | ||
serde = "1.0.110" | ||
serde = { version = "1.0.110", features = ["derive"] } | ||
serde_derive = "1.0.110" | ||
hex = "0.4.2" | ||
|
||
[dev-dependencies] | ||
serde_json = "1.0.52" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
pub mod hex; | ||
pub mod quoted; | ||
pub mod quoted_u64; | ||
pub mod quoted_u64_vec; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
use serde::{Deserializer, Serializer}; | ||
use serde_derive::{Deserialize, Serialize}; | ||
use std::marker::PhantomData; | ||
|
||
/// Serde support for deserializing quoted integers. | ||
/// | ||
/// Configurable so that quotes are either required or optional. | ||
pub struct QuotedIntVisitor<T> { | ||
require_quotes: bool, | ||
_phantom: PhantomData<T>, | ||
} | ||
|
||
impl<'a, T> serde::de::Visitor<'a> for QuotedIntVisitor<T> | ||
where | ||
T: From<u64> + Into<u64> + Copy, | ||
{ | ||
type Value = T; | ||
|
||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { | ||
if self.require_quotes { | ||
write!(formatter, "a quoted integer") | ||
} else { | ||
write!(formatter, "a quoted or unquoted integer") | ||
} | ||
} | ||
|
||
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> | ||
where | ||
E: serde::de::Error, | ||
{ | ||
s.parse::<u64>() | ||
.map(T::from) | ||
.map_err(serde::de::Error::custom) | ||
} | ||
|
||
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E> | ||
where | ||
E: serde::de::Error, | ||
{ | ||
if self.require_quotes { | ||
Err(serde::de::Error::custom( | ||
"received unquoted integer when quotes are required", | ||
)) | ||
} else { | ||
Ok(T::from(v)) | ||
} | ||
} | ||
} | ||
|
||
/// Wrapper type for requiring quotes on a `u64`-like type. | ||
/// | ||
/// Unlike using `serde(with = "quoted_u64::require_quotes")` this is composable, and can be nested | ||
/// inside types like `Option`, `Result` and `Vec`. | ||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)] | ||
#[serde(transparent)] | ||
pub struct Quoted<T> | ||
where | ||
T: From<u64> + Into<u64> + Copy, | ||
{ | ||
#[serde(with = "require_quotes")] | ||
pub value: T, | ||
} | ||
|
||
/// Serialize with quotes. | ||
pub fn serialize<S, T>(value: &T, serializer: S) -> Result<S::Ok, S::Error> | ||
where | ||
S: Serializer, | ||
T: From<u64> + Into<u64> + Copy, | ||
{ | ||
let v: u64 = (*value).into(); | ||
serializer.serialize_str(&format!("{}", v)) | ||
} | ||
|
||
/// Deserialize with or without quotes. | ||
pub fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error> | ||
where | ||
D: Deserializer<'de>, | ||
T: From<u64> + Into<u64> + Copy, | ||
{ | ||
deserializer.deserialize_any(QuotedIntVisitor { | ||
require_quotes: false, | ||
_phantom: PhantomData, | ||
}) | ||
} | ||
|
||
/// Requires quotes when deserializing. | ||
/// | ||
/// Usage: `#[serde(with = "quoted_u64::require_quotes")]`. | ||
pub mod require_quotes { | ||
pub use super::serialize; | ||
use super::*; | ||
|
||
pub fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error> | ||
where | ||
D: Deserializer<'de>, | ||
T: From<u64> + Into<u64> + Copy, | ||
{ | ||
deserializer.deserialize_any(QuotedIntVisitor { | ||
require_quotes: true, | ||
_phantom: PhantomData, | ||
}) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod test { | ||
use super::*; | ||
|
||
#[test] | ||
fn require_quotes() { | ||
let x = serde_json::from_str::<Quoted<u64>>("\"8\"").unwrap(); | ||
assert_eq!(x.value, 8); | ||
serde_json::from_str::<Quoted<u64>>("8").unwrap_err(); | ||
} | ||
} |
Oops, something went wrong.