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

Add seeded versions of deserialize functions. #135

Merged
merged 1 commit into from Oct 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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);
}
}