Merge pull request #1466 from 0nkery/master
Impl Serialize/Deserialize for std::ops::{Bound, RangeFrom, RangeTo, RangeToInclusive}
This commit is contained in:
commit
7a72b4c624
@ -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 {
|
||||
@ -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 {
|
||||
|
@ -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::*;
|
||||
|
||||
@ -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
|
||||
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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};
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -256,6 +256,76 @@ where
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
impl<Idx> Serialize for RangeFrom<Idx>
|
||||
where
|
||||
Idx: Serialize,
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
use super::SerializeStruct;
|
||||
let mut state = try!(serializer.serialize_struct("RangeFrom", 1));
|
||||
try!(state.serialize_field("start", &self.start));
|
||||
state.end()
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
impl<Idx> Serialize for RangeTo<Idx>
|
||||
where
|
||||
Idx: Serialize,
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
use super::SerializeStruct;
|
||||
let mut state = try!(serializer.serialize_struct("RangeTo", 1));
|
||||
try!(state.serialize_field("end", &self.end));
|
||||
state.end()
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#[cfg(range_to_inclusive)]
|
||||
impl<Idx> Serialize for RangeToInclusive<Idx>
|
||||
where
|
||||
Idx: Serialize,
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
use super::SerializeStruct;
|
||||
let mut state = try!(serializer.serialize_struct("RangeToInclusive", 1));
|
||||
try!(state.serialize_field("end", &self.end));
|
||||
state.end()
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[cfg(any(ops_bound, collections_bound))]
|
||||
impl<T> Serialize for Bound<T>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
match *self {
|
||||
Bound::Unbounded => serializer.serialize_unit_variant("Bound", 0, "Unbounded"),
|
||||
Bound::Included(ref value) => serializer.serialize_newtype_variant("Bound", 1, "Included", value),
|
||||
Bound::Excluded(ref value) => serializer.serialize_newtype_variant("Bound", 2, "Excluded", value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
impl Serialize for () {
|
||||
#[inline]
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
|
@ -10,6 +10,7 @@ use std::path::{Path, PathBuf};
|
||||
use std::rc::{Rc, Weak as RcWeak};
|
||||
use std::sync::{Arc, Weak as ArcWeak};
|
||||
use std::time::{Duration, UNIX_EPOCH};
|
||||
use std::ops::Bound;
|
||||
|
||||
use fnv::FnvHasher;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
@ -836,6 +837,47 @@ declare_tests! {
|
||||
Token::SeqEnd,
|
||||
],
|
||||
}
|
||||
test_range_from {
|
||||
0u8.. => &[
|
||||
Token::Struct { name: "RangeFrom", len: 1 },
|
||||
Token::Str("start"),
|
||||
Token::U8(0),
|
||||
Token::StructEnd,
|
||||
],
|
||||
}
|
||||
test_range_to {
|
||||
..2u8 => &[
|
||||
Token::Struct { name: "RangeTo", len: 1 },
|
||||
Token::Str("end"),
|
||||
Token::U8(2),
|
||||
Token::StructEnd,
|
||||
],
|
||||
}
|
||||
test_range_to_inclusive {
|
||||
..=2u8 => &[
|
||||
Token::Struct { name: "RangeToInclusive", len: 1 },
|
||||
Token::Str("end"),
|
||||
Token::U8(2),
|
||||
Token::StructEnd,
|
||||
],
|
||||
}
|
||||
test_bound {
|
||||
Bound::Unbounded::<()> => &[
|
||||
Token::Enum { name: "Bound" },
|
||||
Token::Str("Unbounded"),
|
||||
Token::Unit,
|
||||
],
|
||||
Bound::Included(0) => &[
|
||||
Token::Enum { name: "Bound" },
|
||||
Token::Str("Included"),
|
||||
Token::U8(0),
|
||||
],
|
||||
Bound::Excluded(0) => &[
|
||||
Token::Enum { name: "Bound" },
|
||||
Token::Str("Excluded"),
|
||||
Token::U8(0),
|
||||
],
|
||||
}
|
||||
test_path {
|
||||
Path::new("/usr/local/lib") => &[
|
||||
Token::BorrowedStr("/usr/local/lib"),
|
||||
|
@ -10,6 +10,7 @@ use std::path::{Path, PathBuf};
|
||||
use std::rc::{Rc, Weak as RcWeak};
|
||||
use std::sync::{Arc, Weak as ArcWeak};
|
||||
use std::time::{Duration, UNIX_EPOCH};
|
||||
use std::ops::Bound;
|
||||
|
||||
#[cfg(unix)]
|
||||
use std::str;
|
||||
@ -375,6 +376,47 @@ declare_tests! {
|
||||
Token::StructEnd,
|
||||
],
|
||||
}
|
||||
test_range_from {
|
||||
0u8.. => &[
|
||||
Token::Struct { name: "RangeFrom", len: 1 },
|
||||
Token::Str("start"),
|
||||
Token::U8(0),
|
||||
Token::StructEnd,
|
||||
],
|
||||
}
|
||||
test_range_to {
|
||||
..2u8 => &[
|
||||
Token::Struct { name: "RangeTo", len: 1 },
|
||||
Token::Str("end"),
|
||||
Token::U8(2),
|
||||
Token::StructEnd,
|
||||
],
|
||||
}
|
||||
test_range_to_inclusive {
|
||||
..=2u8 => &[
|
||||
Token::Struct { name: "RangeToInclusive", len: 1 },
|
||||
Token::Str("end"),
|
||||
Token::U8(2),
|
||||
Token::StructEnd,
|
||||
],
|
||||
}
|
||||
test_bound {
|
||||
Bound::Unbounded::<()> => &[
|
||||
Token::Enum { name: "Bound" },
|
||||
Token::Str("Unbounded"),
|
||||
Token::Unit,
|
||||
],
|
||||
Bound::Included(0u8) => &[
|
||||
Token::Enum { name: "Bound" },
|
||||
Token::Str("Included"),
|
||||
Token::U8(0),
|
||||
],
|
||||
Bound::Excluded(0u8) => &[
|
||||
Token::Enum { name: "Bound" },
|
||||
Token::Str("Excluded"),
|
||||
Token::U8(0),
|
||||
],
|
||||
}
|
||||
test_path {
|
||||
Path::new("/usr/local/lib") => &[
|
||||
Token::Str("/usr/local/lib"),
|
||||
|
Loading…
x
Reference in New Issue
Block a user