Skip to content
This repository has been archived by the owner on Mar 25, 2024. It is now read-only.

Commit

Permalink
Merge pull request #135 from toasteater/feature/de-seed
Browse files Browse the repository at this point in the history
Add seeded versions of deserialize functions.
  • Loading branch information
dtolnay committed Oct 4, 2019
2 parents 6c2a10f + 36266b8 commit 14ea96f
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 21 deletions.
120 changes: 99 additions & 21 deletions src/de.rs
Expand Up @@ -997,20 +997,9 @@ impl<'de, 'a, 'r> de::Deserializer<'de> for &'r mut Deserializer<'a> {
}
}

/// Deserialize an instance of type `T` from a string of YAML text.
///
/// This conversion can fail if the structure of the Value does not match the
/// structure expected by `T`, for example if `T` is a struct type but the Value
/// contains something other than a YAML map. It can also fail if the structure
/// is correct but `T`'s implementation of `Deserialize` decides that something
/// is wrong with the data, for example required struct fields are missing from
/// the YAML map or some number is too big to fit in the expected primitive
/// type.
///
/// YAML currently does not support zero-copy deserialization.
pub fn from_str<T>(s: &str) -> Result<T>
fn from_str_with<F, U>(s: &str, op: F) -> Result<U>
where
T: DeserializeOwned,
F: FnOnce(&mut Deserializer) -> Result<U>,
{
let mut parser = Parser::new(s.chars());
let mut loader = Loader {
Expand All @@ -1024,7 +1013,7 @@ where
Err(private::error_end_of_stream())
} else {
let mut pos = 0;
let t = Deserialize::deserialize(&mut Deserializer {
let t = op(&mut Deserializer {
events: &loader.events,
aliases: &loader.aliases,
pos: &mut pos,
Expand All @@ -1039,6 +1028,64 @@ where
}
}

fn from_reader_with<R, F, U>(mut rdr: R, op: F) -> Result<U>
where
R: io::Read,
F: FnOnce(&str) -> Result<U>,
{
let mut bytes = Vec::new();
rdr.read_to_end(&mut bytes).map_err(private::error_io)?;
let s = str::from_utf8(&bytes).map_err(private::error_str_utf8)?;
op(s)
}

fn from_slice_with<F, U>(v: &[u8], op: F) -> Result<U>
where
F: FnOnce(&str) -> Result<U>,
{
let s = str::from_utf8(v).map_err(private::error_str_utf8)?;
op(s)
}

/// Deserialize an instance of type `T` from a string of YAML text.
///
/// This conversion can fail if the structure of the Value does not match the
/// structure expected by `T`, for example if `T` is a struct type but the Value
/// contains something other than a YAML map. It can also fail if the structure
/// is correct but `T`'s implementation of `Deserialize` decides that something
/// is wrong with the data, for example required struct fields are missing from
/// the YAML map or some number is too big to fit in the expected primitive
/// type.
///
/// YAML currently does not support zero-copy deserialization.
pub fn from_str<T>(s: &str) -> Result<T>
where
T: DeserializeOwned,
{
// This closure is required because Rust can't currently seem to understand
// that a for<T: Trait> fn(T) is also a FnOnce(Type) where Type: Trait.
#[allow(redundant_closure)]
from_str_with(s, |de| Deserialize::deserialize(de))
}

/// Deserialize an instance of type `T` from a string of YAML text with a seed.
///
/// This conversion can fail if the structure of the Value does not match the
/// structure expected by `T`, for example if `T` is a struct type but the Value
/// contains something other than a YAML map. It can also fail if the structure
/// is correct but `T`'s implementation of `Deserialize` decides that something
/// is wrong with the data, for example required struct fields are missing from
/// the YAML map or some number is too big to fit in the expected primitive
/// type.
///
/// YAML currently does not support zero-copy deserialization.
pub fn from_str_seed<T, S>(s: &str, seed: S) -> Result<T>
where
S: for<'de> DeserializeSeed<'de, Value = T>,
{
from_str_with(s, |de| seed.deserialize(de))
}

/// Deserialize an instance of type `T` from an IO stream of YAML.
///
/// This conversion can fail if the structure of the Value does not match the
Expand All @@ -1048,15 +1095,29 @@ where
/// is wrong with the data, for example required struct fields are missing from
/// the YAML map or some number is too big to fit in the expected primitive
/// type.
pub fn from_reader<R, T>(mut rdr: R) -> Result<T>
pub fn from_reader<R, T>(rdr: R) -> Result<T>
where
R: io::Read,
T: DeserializeOwned,
{
let mut bytes = Vec::new();
rdr.read_to_end(&mut bytes).map_err(private::error_io)?;
let s = str::from_utf8(&bytes).map_err(private::error_str_utf8)?;
from_str(s)
from_reader_with(rdr, from_str)
}

/// Deserialize an instance of type `T` from an IO stream of YAML with a seed.
///
/// This conversion can fail if the structure of the Value does not match the
/// structure expected by `T`, for example if `T` is a struct type but the Value
/// contains something other than a YAML map. It can also fail if the structure
/// is correct but `T`'s implementation of `Deserialize` decides that something
/// is wrong with the data, for example required struct fields are missing from
/// the YAML map or some number is too big to fit in the expected primitive
/// type.
pub fn from_reader_seed<R, T, S>(rdr: R, seed: S) -> Result<T>
where
R: io::Read,
S: for<'de> DeserializeSeed<'de, Value = T>,
{
from_reader_with(rdr, |s| from_str_seed(s, seed))
}

/// Deserialize an instance of type `T` from bytes of YAML text.
Expand All @@ -1074,6 +1135,23 @@ pub fn from_slice<T>(v: &[u8]) -> Result<T>
where
T: DeserializeOwned,
{
let s = str::from_utf8(v).map_err(private::error_str_utf8)?;
from_str(s)
from_slice_with(v, from_str)
}

/// Deserialize an instance of type `T` from bytes of YAML text with a seed.
///
/// This conversion can fail if the structure of the Value does not match the
/// structure expected by `T`, for example if `T` is a struct type but the Value
/// contains something other than a YAML map. It can also fail if the structure
/// is correct but `T`'s implementation of `Deserialize` decides that something
/// is wrong with the data, for example required struct fields are missing from
/// the YAML map or some number is too big to fit in the expected primitive
/// type.
///
/// YAML currently does not support zero-copy deserialization.
pub fn from_slice_seed<T, S>(v: &[u8], seed: S) -> Result<T>
where
S: for<'de> DeserializeSeed<'de, Value = T>,
{
from_slice_with(v, |s| from_str_seed(s, seed))
}
5 changes: 5 additions & 0 deletions src/lib.rs
Expand Up @@ -99,6 +99,11 @@ pub use self::mapping::Mapping;
pub use self::ser::{to_string, to_vec, to_writer};
pub use self::value::{from_value, to_value, Index, Number, Sequence, Value};

/// Deserialization with seeds
pub mod seed {
pub use super::de::{from_reader_seed, from_slice_seed, from_str_seed};
}

mod de;
mod error;
mod mapping;
Expand Down
49 changes: 49 additions & 0 deletions tests/test_de.rs
Expand Up @@ -25,6 +25,18 @@ where
serde_yaml::from_str::<serde::de::IgnoredAny>(yaml).unwrap();
}

fn test_de_seed<T, S>(yaml: &str, seed: S, expected: &T)
where
T: PartialEq + Debug,
S: for<'de> serde::de::DeserializeSeed<'de, Value = T>,
{
let deserialized: T = serde_yaml::seed::from_str_seed(yaml, seed).unwrap();
assert_eq!(*expected, deserialized);

serde_yaml::from_str::<serde_yaml::Value>(yaml).unwrap();
serde_yaml::from_str::<serde::de::IgnoredAny>(yaml).unwrap();
}

#[test]
fn test_alias() {
let yaml = unindent(
Expand Down Expand Up @@ -352,3 +364,40 @@ fn test_numbers() {
}
}
}

#[test]
fn test_stateful() {
struct Seed(i64);

impl<'de> serde::de::DeserializeSeed<'de> for Seed {
type Value = i64;
fn deserialize<D>(self, deserializer: D) -> Result<i64, D::Error>
where
D: serde::de::Deserializer<'de>,
{
struct Visitor(i64);
impl<'de> serde::de::Visitor<'de> for Visitor {
type Value = i64;

fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "an integer")
}

fn visit_i64<E: serde::de::Error>(self, v: i64) -> Result<i64, E> {
Ok(v * self.0)
}

fn visit_u64<E: serde::de::Error>(self, v: u64) -> Result<i64, E> {
Ok(v as i64 * self.0)
}
}

deserializer.deserialize_any(Visitor(self.0))
}
}

let cases = [("3", 5, 15), ("6", 7, 42), ("-5", 9, -45)];
for &(yaml, seed, expected) in &cases {
test_de_seed(yaml, Seed(seed), &expected);
}
}

0 comments on commit 14ea96f

Please sign in to comment.