Skip to content

Commit

Permalink
Merge pull request #1466 from 0nkery/master
Browse files Browse the repository at this point in the history
Impl Serialize/Deserialize for std::ops::{Bound, RangeFrom, RangeTo, RangeToInclusive}
  • Loading branch information
dtolnay committed Feb 2, 2019
2 parents 670c179 + 18b1604 commit 7a72b4c
Show file tree
Hide file tree
Showing 6 changed files with 434 additions and 2 deletions.
15 changes: 15 additions & 0 deletions serde/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ fn main() {
let target = env::var("TARGET").unwrap();
let emscripten = target == "asmjs-unknown-emscripten" || target == "wasm32-unknown-emscripten";

// std::collections::Bound was stabilized in Rust 1.17
// but it was moved to core::ops later in Rust 1.26:
// https://doc.rust-lang.org/core/ops/enum.Bound.html
if minor >= 26 {
println!("cargo:rustc-cfg=ops_bound");
} else if minor >= 17 && cfg!(feature = "std") {
println!("cargo:rustc-cfg=collections_bound");
}

// CString::into_boxed_c_str stabilized in Rust 1.20:
// https://doc.rust-lang.org/std/ffi/struct.CString.html#method.into_boxed_c_str
if minor >= 20 {
Expand Down Expand Up @@ -42,6 +51,12 @@ fn main() {
println!("cargo:rustc-cfg=integer128");
}

// RangeToInclusive was stabilized in Rust 1.26:
// https://doc.rust-lang.org/std/ops/struct.RangeToInclusive.html
if minor >= 26 {
println!("cargo:rustc-cfg=range_to_inclusive");
}

// Inclusive ranges methods stabilized in Rust 1.27:
// https://github.com/rust-lang/rust/pull/50758
if minor >= 27 {
Expand Down
256 changes: 255 additions & 1 deletion serde/src/de/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2138,6 +2138,73 @@ where
}
}

impl<'de, Idx> Deserialize<'de> for RangeFrom<Idx>
where
Idx: Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let field = range::Field::Start;
let start = deserializer.deserialize_struct(
"RangeFrom",
field.name_slice(),
range::UnboundedRangeVisitor {
expecting: "struct RangeFrom",
phantom: PhantomData,
field: field,
},
)?;
Ok(start..)
}
}

impl<'de, Idx> Deserialize<'de> for RangeTo<Idx>
where
Idx: Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let field = range::Field::End;
let end = deserializer.deserialize_struct(
"RangeTo",
field.name_slice(),
range::UnboundedRangeVisitor {
expecting: "struct RangeTo",
phantom: PhantomData,
field: field,
},
)?;
Ok(..end)
}
}

#[cfg(range_to_inclusive)]
impl<'de, Idx> Deserialize<'de> for RangeToInclusive<Idx>
where
Idx: Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let field = range::Field::End;
let end = deserializer.deserialize_struct(
"RangeToInclusive",
field.name_slice(),
range::UnboundedRangeVisitor {
expecting: "struct RangeToInclusive",
phantom: PhantomData,
field: field,
},
)?;
Ok(RangeToInclusive { end: end })
}
}

mod range {
use lib::*;

Expand All @@ -2149,11 +2216,31 @@ mod range {
//
// #[derive(Deserialize)]
// #[serde(field_identifier, rename_all = "lowercase")]
enum Field {
#[derive(PartialEq)]
pub enum Field {
Start,
End,
}

const FIELDS_START_ONLY: &'static [&'static str] = &["start"];
const FIELD_END_ONLY: &'static [&'static str] = &["end"];

impl Field {
fn name(&self) -> &'static str {
match *self {
Field::Start => "start",
Field::End => "end",
}
}

pub fn name_slice(&self) -> &'static [&'static str] {
match *self {
Field::Start => FIELDS_START_ONLY,
Field::End => FIELD_END_ONLY,
}
}
}

impl<'de> Deserialize<'de> for Field {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
Expand Down Expand Up @@ -2265,6 +2352,173 @@ mod range {
Ok((start, end))
}
}

pub struct UnboundedRangeVisitor<Idx> {
pub expecting: &'static str,
pub phantom: PhantomData<Idx>,
pub field: Field,
}

impl<'de, Idx> Visitor<'de> for UnboundedRangeVisitor<Idx>
where
Idx: Deserialize<'de>,
{
type Value = Idx;

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str(self.expecting)
}

fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let value: Idx = match try!(seq.next_element()) {
Some(value) => value,
None => {
return Err(Error::invalid_length(0, &self));
}
};
Ok(value)
}

fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let mut value: Option<Idx> = None;
while let Some(key) = try!(map.next_key()) {
let key: Field = key;
match key {
ref key if *key == self.field => {
if value.is_some() {
return Err(<A::Error as Error>::duplicate_field(key.name()));
}
value = Some(try!(map.next_value()));
}
key => {
return Err(<A::Error as Error>::unknown_field(key.name(), self.field.name_slice()));
}
}
}
let value = match value {
Some(value) => value,
None => return Err(<A::Error as Error>::missing_field(self.field.name())),
};
Ok(value)
}
}
}

////////////////////////////////////////////////////////////////////////////////

#[cfg(any(ops_bound, collections_bound))]
impl<'de, T> Deserialize<'de> for Bound<T>
where
T: Deserialize<'de>
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
enum Field {
Unbounded,
Included,
Excluded,
}

impl<'de> Deserialize<'de> for Field {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct FieldVisitor;

impl<'de> Visitor<'de> for FieldVisitor {
type Value = Field;

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("`Unbounded`, `Included` or `Excluded`")
}

fn visit_u32<E>(self, value: u32) -> Result<Self::Value, E>
where
E: Error,
{
match value {
0 => Ok(Field::Unbounded),
1 => Ok(Field::Included),
2 => Ok(Field::Excluded),
_ => Err(Error::invalid_value(
Unexpected::Unsigned(value as u64),
&self,
)),
}
}

fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: Error,
{
match value {
"Unbounded" => Ok(Field::Unbounded),
"Included" => Ok(Field::Included),
"Excluded" => Ok(Field::Excluded),
_ => Err(Error::unknown_variant(value, VARIANTS)),
}
}

fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
where
E: Error,
{
match value {
b"Unbounded" => Ok(Field::Unbounded),
b"Included" => Ok(Field::Included),
b"Excluded" => Ok(Field::Excluded),
_ => match str::from_utf8(value) {
Ok(value) => Err(Error::unknown_variant(value, VARIANTS)),
Err(_) => {
Err(Error::invalid_value(Unexpected::Bytes(value), &self))
}
},
}
}
}

deserializer.deserialize_identifier(FieldVisitor)
}
}

struct BoundVisitor<T>(PhantomData<Bound<T>>);

impl<'de, T> Visitor<'de> for BoundVisitor<T>
where
T: Deserialize<'de>,
{
type Value = Bound<T>;

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("enum Bound")
}

fn visit_enum<A>(self, data: A) -> Result<Self::Value, A::Error>
where
A: EnumAccess<'de>,
{
match try!(data.variant()) {
(Field::Unbounded, v) => v.newtype_variant().map(|_: T| Bound::Unbounded),
(Field::Included, v) => v.newtype_variant().map(Bound::Included),
(Field::Excluded, v) => v.newtype_variant().map(Bound::Excluded),
}
}
}

const VARIANTS: &'static [&'static str] = &["Unbounded", "Included", "Excluded"];

deserializer.deserialize_enum("Bound", VARIANTS, BoundVisitor(PhantomData))
}
}

////////////////////////////////////////////////////////////////////////////////
Expand Down
11 changes: 10 additions & 1 deletion serde/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ mod lib {
pub use self::core::default::{self, Default};
pub use self::core::fmt::{self, Debug, Display};
pub use self::core::marker::{self, PhantomData};
pub use self::core::ops::Range;
pub use self::core::ops::{Range, RangeFrom, RangeTo};
pub use self::core::option::{self, Option};
pub use self::core::result::{self, Result};

Expand Down Expand Up @@ -207,6 +207,15 @@ mod lib {

#[cfg(range_inclusive)]
pub use self::core::ops::RangeInclusive;

#[cfg(all(feature = "std", collections_bound))]
pub use std::collections::Bound;

#[cfg(ops_bound)]
pub use self::core::ops::Bound;

#[cfg(range_to_inclusive)]
pub use self::core::ops::RangeToInclusive;
}

////////////////////////////////////////////////////////////////////////////////
Expand Down
Loading

0 comments on commit 7a72b4c

Please sign in to comment.