diff --git a/serde_codegen/src/de.rs b/serde_codegen/src/de.rs index f1274bca..eda81859 100644 --- a/serde_codegen/src/de.rs +++ b/serde_codegen/src/de.rs @@ -1,5 +1,5 @@ use syn::{self, aster, Ident}; -use quote::Tokens; +use quote::{self, Tokens}; use bound; use internals::ast::{Body, Field, Item, Style, Variant}; @@ -682,7 +682,8 @@ fn deserialize_field_visitor( item_attrs: &attr::Item, is_variant: bool, ) -> Tokens { - let field_names = fields.iter().map(|&(ref name, _)| name); + let field_strs = fields.iter().map(|&(ref name, _)| name); + let field_bytes = fields.iter().map(|&(ref name, _)| quote::ByteStr(name)); let field_idents: &Vec<_> = &fields.iter().map(|&(_, ref ident)| ident).collect(); let ignore_variant = if is_variant || item_attrs.deny_unknown_fields() { @@ -691,6 +692,27 @@ fn deserialize_field_visitor( Some(quote!(__ignore,)) }; + let visit_usize = if is_variant { + let variant_indices = 0usize..; + let fallthrough_msg = format!("variant index 0 <= i < {}", fields.len()); + Some(quote! { + fn visit_usize<__E>(self, value: usize) -> ::std::result::Result<__Field, __E> + where __E: _serde::de::Error + { + match value { + #( + #variant_indices => Ok(__Field::#field_idents), + )* + _ => Err(_serde::de::Error::invalid_value( + _serde::de::Unexpected::Unsigned(value as u64), + &#fallthrough_msg)) + } + } + }) + } else { + None + }; + let fallthrough_arm = if is_variant { quote! { Err(_serde::de::Error::unknown_variant(value, VARIANTS)) @@ -705,6 +727,16 @@ fn deserialize_field_visitor( } }; + let bytes_to_str = if is_variant || item_attrs.deny_unknown_fields() { + Some(quote! { + // TODO https://github.com/serde-rs/serde/issues/666 + // update this to use str::from_utf8(value).unwrap_or("���") on no_std + let value = &::std::string::String::from_utf8_lossy(value); + }) + } else { + None + }; + quote! { #[allow(non_camel_case_types)] enum __Field { @@ -726,16 +758,32 @@ fn deserialize_field_visitor( formatter.write_str("field name") } + #visit_usize + fn visit_str<__E>(self, value: &str) -> ::std::result::Result<__Field, __E> where __E: _serde::de::Error { match value { #( - #field_names => Ok(__Field::#field_idents), + #field_strs => Ok(__Field::#field_idents), )* _ => #fallthrough_arm } } + + fn visit_bytes<__E>(self, value: &[u8]) -> ::std::result::Result<__Field, __E> + where __E: _serde::de::Error + { + match value { + #( + #field_bytes => Ok(__Field::#field_idents), + )* + _ => { + #bytes_to_str + #fallthrough_arm + } + } + } } deserializer.deserialize_struct_field(__FieldVisitor) diff --git a/testing/tests/test_de.rs b/testing/tests/test_de.rs index 451d3d89..83c3eabf 100644 --- a/testing/tests/test_de.rs +++ b/testing/tests/test_de.rs @@ -774,6 +774,20 @@ declare_tests! { Token::EnumMapEnd, ], } + test_enum_unit_usize { + Enum::Unit => &[ + Token::EnumStart("Enum"), + Token::Usize(0), + Token::Unit, + ], + } + test_enum_unit_bytes { + Enum::Unit => &[ + Token::EnumStart("Enum"), + Token::Bytes(b"Unit"), + Token::Unit, + ], + } test_box { Box::new(0i32) => &[Token::I32(0)], } @@ -918,20 +932,12 @@ declare_error_tests! { ], Error::Message("duplicate field `a`".to_owned()), } - test_enum_unit_usize { + test_enum_out_of_range { &[ Token::EnumStart("Enum"), - Token::Usize(0), + Token::Usize(4), Token::Unit, ], - Error::Message("invalid type: integer `0`, expected field name".into()), - } - test_enum_unit_bytes { - &[ - Token::EnumStart("Enum"), - Token::Bytes(b"Unit"), - Token::Unit, - ], - Error::Message("invalid type: byte array, expected field name".into()), + Error::Message("invalid value: integer `4`, expected variant index 0 <= i < 4".into()), } }