Skip to content

Commit

Permalink
Allow borrow for field identifiers
Browse files Browse the repository at this point in the history
  • Loading branch information
Mingun committed Oct 23, 2020
1 parent 9e1f573 commit 7a7a182
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 63 deletions.
44 changes: 33 additions & 11 deletions serde/src/private/de.rs
@@ -1,7 +1,7 @@
use lib::*;

use de::{Deserialize, DeserializeSeed, Deserializer, Error, IntoDeserializer, Visitor};
use de::value::BytesDeserializer;
use de::value::{BytesDeserializer, BorrowedBytesDeserializer};

#[cfg(any(feature = "std", feature = "alloc"))]
use de::{MapAccess, Unexpected};
Expand Down Expand Up @@ -2527,55 +2527,77 @@ mod content {

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

// Like `IntoDeserializer` but also implemented for `&[u8]`. This is used for
// the newtype fallthrough case of `field_identifier`.
//
// #[derive(Deserialize)]
// #[serde(field_identifier)]
// enum F {
// A,
// B,
// Other(String), // deserialized using IdentifierDeserializer
// }
/// Like `IntoDeserializer` but also implemented for `&[u8]`. This is used for
/// the newtype fallthrough case of `field_identifier`.
///
/// ```ignore
/// #[derive(Deserialize)]
/// #[serde(field_identifier)]
/// enum F {
/// A,
/// B,
/// Other(String), // deserialized using IdentifierDeserializer
/// }
/// ```
pub trait IdentifierDeserializer<'de, E: Error> {
/// Deserializer, that refers to data owned by deserializer
type Deserializer: Deserializer<'de, Error = E>;
/// Deserializer, that borrows data from the input
type BorrowedDeserializer: Deserializer<'de, Error = E>;

fn from(self) -> Self::Deserializer;
fn borrowed(self) -> Self::BorrowedDeserializer;
}

impl<'de, E> IdentifierDeserializer<'de, E> for u32
where
E: Error,
{
type Deserializer = <u32 as IntoDeserializer<'de, E>>::Deserializer;
type BorrowedDeserializer = <u32 as IntoDeserializer<'de, E>>::Deserializer;

fn from(self) -> Self::Deserializer {
self.into_deserializer()
}

fn borrowed(self) -> Self::BorrowedDeserializer {
self.into_deserializer()
}
}

forward_deserializer!(ref StrDeserializer<'a>(&'a str) => visit_str);
forward_deserializer!(borrowed BorrowedStrDeserializer(&'de str) => visit_borrowed_str);

impl<'a, E> IdentifierDeserializer<'a, E> for &'a str
where
E: Error,
{
type Deserializer = StrDeserializer<'a, E>;
type BorrowedDeserializer = BorrowedStrDeserializer<'a, E>;

fn from(self) -> Self::Deserializer {
StrDeserializer::new(self)
}

fn borrowed(self) -> Self::BorrowedDeserializer {
BorrowedStrDeserializer::new(self)
}
}

impl<'a, E> IdentifierDeserializer<'a, E> for &'a [u8]
where
E: Error,
{
type Deserializer = BytesDeserializer<'a, E>;
type BorrowedDeserializer = BorrowedBytesDeserializer<'a, E>;

fn from(self) -> Self::Deserializer {
BytesDeserializer::new(self)
}

fn borrowed(self) -> Self::BorrowedDeserializer {
BorrowedBytesDeserializer::new(self)
}
}

/// A DeserializeSeed helper for implementing deserialize_in_place Visitors.
Expand Down
127 changes: 75 additions & 52 deletions serde_derive/src/de.rs
Expand Up @@ -1886,17 +1886,26 @@ fn deserialize_generated_identifier(
let (ignore_variant, fallthrough) = if !is_variant && cattrs.has_flatten() {
let ignore_variant = quote!(__other(_serde::private::de::Content<'de>),);
let fallthrough = quote!(_serde::export::Ok(__Field::__other(__value)));
(Some(ignore_variant), Some(fallthrough))
(
Some(ignore_variant),
Some((fallthrough.clone(), fallthrough))
)
} else if let Some(other_idx) = other_idx {
let ignore_variant = fields[other_idx].1.clone();
let fallthrough = quote!(_serde::export::Ok(__Field::#ignore_variant));
(None, Some(fallthrough))
(
None,
Some((fallthrough.clone(), fallthrough))
)
} else if is_variant || cattrs.deny_unknown_fields() {
(None, None)
} else {
let ignore_variant = quote!(__ignore,);
let fallthrough = quote!(_serde::export::Ok(__Field::__ignore));
(Some(ignore_variant), Some(fallthrough))
(
Some(ignore_variant),
Some((fallthrough.clone(), fallthrough))
)
};

let visitor_impl = Stmts(deserialize_identifier(
Expand Down Expand Up @@ -1959,16 +1968,27 @@ fn deserialize_custom_identifier(
if last.attrs.other() {
let ordinary = &variants[..variants.len() - 1];
let fallthrough = quote!(_serde::export::Ok(#this::#last_ident));
(ordinary, Some(fallthrough))
(
ordinary,
Some((fallthrough.clone(), fallthrough))
)
} else if let Style::Newtype = last.style {
let ordinary = &variants[..variants.len() - 1];
let deserializer = quote!(_serde::private::de::IdentifierDeserializer::from(__value));
let fallthrough = quote! {

let fallthrough = |method| quote! {
_serde::export::Result::map(
_serde::Deserialize::deserialize(#deserializer),
_serde::Deserialize::deserialize(
_serde::private::de::IdentifierDeserializer::#method(__value)
),
#this::#last_ident)
};
(ordinary, Some(fallthrough))
(
ordinary,
Some((
fallthrough(quote!(from)),
fallthrough(quote!(borrowed)),
))
)
} else {
(variants, None)
}
Expand Down Expand Up @@ -2040,22 +2060,20 @@ fn deserialize_identifier(
this: &TokenStream,
fields: &[(String, Ident, Vec<String>)],
is_variant: bool,
fallthrough: Option<TokenStream>,
// .0 for referenced data, .1 -- for borrowed
fallthrough: Option<(TokenStream, TokenStream)>,
collect_other_fields: bool,
) -> Fragment {
let mut flat_fields = Vec::new();
for (_, ident, aliases) in fields {
flat_fields.extend(aliases.iter().map(|alias| (alias, ident)))
}

let field_strs = flat_fields.iter().map(|(name, _)| name);
let field_borrowed_strs = flat_fields.iter().map(|(name, _)| name);
let field_bytes = flat_fields
.iter()
.map(|(name, _)| Literal::byte_string(name.as_bytes()));
let field_borrowed_bytes = flat_fields
let field_strs: &Vec<_> = &flat_fields.iter().map(|(name, _)| name).collect();
let field_bytes: &Vec<_> = &flat_fields
.iter()
.map(|(name, _)| Literal::byte_string(name.as_bytes()));
.map(|(name, _)| Literal::byte_string(name.as_bytes()))
.collect();

let constructors: &Vec<_> = &flat_fields
.iter()
Expand Down Expand Up @@ -2106,16 +2124,21 @@ fn deserialize_identifier(
(None, None, None, None)
};

let fallthrough_arm = if let Some(fallthrough) = fallthrough {
let (
fallthrough_arm,
fallthrough_borrowed_arm,
) = if let Some(fallthrough) = fallthrough.clone() {
fallthrough
} else if is_variant {
quote! {
let fallthrough = quote! {
_serde::export::Err(_serde::de::Error::unknown_variant(__value, VARIANTS))
}
};
(fallthrough.clone(), fallthrough)
} else {
quote! {
let fallthrough = quote! {
_serde::export::Err(_serde::de::Error::unknown_field(__value, FIELDS))
}
};
(fallthrough.clone(), fallthrough)
};

let variant_indices = 0_u64..;
Expand Down Expand Up @@ -2212,37 +2235,6 @@ fn deserialize_identifier(
{
_serde::export::Ok(__Field::__other(_serde::private::de::Content::Unit))
}

fn visit_borrowed_str<__E>(self, __value: &'de str) -> _serde::export::Result<Self::Value, __E>
where
__E: _serde::de::Error,
{
match __value {
#(
#field_borrowed_strs => _serde::export::Ok(#constructors),
)*
_ => {
#value_as_borrowed_str_content
#fallthrough_arm
}
}
}

fn visit_borrowed_bytes<__E>(self, __value: &'de [u8]) -> _serde::export::Result<Self::Value, __E>
where
__E: _serde::de::Error,
{
match __value {
#(
#field_borrowed_bytes => _serde::export::Ok(#constructors),
)*
_ => {
#bytes_to_str
#value_as_borrowed_bytes_content
#fallthrough_arm
}
}
}
}
} else {
quote! {
Expand Down Expand Up @@ -2285,6 +2277,21 @@ fn deserialize_identifier(
}
}

fn visit_borrowed_str<__E>(self, __value: &'de str) -> _serde::export::Result<Self::Value, __E>
where
__E: _serde::de::Error,
{
match __value {
#(
#field_strs => _serde::export::Ok(#constructors),
)*
_ => {
#value_as_borrowed_str_content
#fallthrough_borrowed_arm
}
}
}

fn visit_bytes<__E>(self, __value: &[u8]) -> _serde::export::Result<Self::Value, __E>
where
__E: _serde::de::Error,
Expand All @@ -2300,6 +2307,22 @@ fn deserialize_identifier(
}
}
}

fn visit_borrowed_bytes<__E>(self, __value: &'de [u8]) -> _serde::export::Result<Self::Value, __E>
where
__E: _serde::de::Error,
{
match __value {
#(
#field_bytes => _serde::export::Ok(#constructors),
)*
_ => {
#bytes_to_str
#value_as_borrowed_bytes_content
#fallthrough_borrowed_arm
}
}
}
}
}

Expand Down
31 changes: 31 additions & 0 deletions test_suite/tests/test_borrow.rs
Expand Up @@ -90,6 +90,37 @@ fn test_struct() {
);
}

#[test]
fn test_field_identifier() {
#[derive(Deserialize, Debug, PartialEq)]
#[serde(field_identifier)]
enum FieldStr<'a> {
#[serde(borrow)]
Str(&'a str),
}

assert_de_tokens(
&FieldStr::Str("value"),
&[
Token::BorrowedStr("value"),
],
);

#[derive(Deserialize, Debug, PartialEq)]
#[serde(field_identifier)]
enum FieldBytes<'a> {
#[serde(borrow)]
Bytes(&'a [u8]),
}

assert_de_tokens(
&FieldBytes::Bytes(b"value"),
&[
Token::BorrowedBytes(b"value"),
],
);
}

#[test]
fn test_cow() {
#[derive(Deserialize)]
Expand Down

0 comments on commit 7a7a182

Please sign in to comment.