Compare commits

...

91 Commits

Author SHA1 Message Date
David Tolnay 89c4b02bf3 Release 1.0.210 2024-09-06 11:17:20 -07:00
David Tolnay eeb8e44cda Merge pull request #2818 from dtolnay/coreerror
Stabilize no-std Error trait
2024-09-06 11:16:43 -07:00
David Tolnay 785c2d9605 Stabilize no-std StdError trait 2024-09-06 11:12:13 -07:00
David Tolnay d549f048e1 Reformat parse_ip_impl definition and calls 2024-09-06 11:05:54 -07:00
David Tolnay 4c0dd63011 Delete attr support from core::net deserialization macros 2024-09-06 11:05:54 -07:00
David Tolnay 26fb134165 Relocate cfg attrs out of parse_ip_impl and parse_socket_impl 2024-09-06 11:05:53 -07:00
David Tolnay 07e614b52b Merge pull request #2817 from dtolnay/corenet
Delete doc(cfg) attribute from impls that are supported in no-std
2024-09-06 11:05:48 -07:00
David Tolnay b1f899fbe8 Delete doc(cfg) attribute from impls that are supported in no-std 2024-09-06 11:00:55 -07:00
David Tolnay b4f860e627 Merge pull request #2816 from MathiasKoch/chore/core-net
Implement serialize/deserialize for core::net instead of std::net
2024-09-06 10:59:24 -07:00
Mathias d940fe1b49 Reuse existing Buf wrapper as replacement for std::io::Write 2024-09-05 14:24:44 +02:00
Mathias f2899a9e06 Implement serialize/deserialize for core::net instead of std::net if running rust version newer than 1.77, where core::net was stabilized 2024-09-05 11:07:09 +02:00
David Tolnay 3aca38d2d3 Upload CI Cargo.lock for reproducing failures 2024-08-25 12:12:25 -07:00
David Tolnay 30752ac4ff Release 1.0.209 2024-08-23 20:29:05 -07:00
David Tolnay b84e6ca4f5 Improve wording of PR 2805 comments 2024-08-23 20:28:11 -07:00
David Tolnay 87a2fb0f1a Wrap comments from PR 2805 to 80 columns 2024-08-23 20:25:16 -07:00
David Tolnay 9eaf7b9824 Merge pull request #2805 from Mingun/untagged-tests
Fix deserialization of empty structs and tuples in untagged enums
2024-08-23 20:25:07 -07:00
Mingun 7bde100237 Replace MapRefDeserializer with value::MapDeserializer
Although they are slightly different, this difference is irrelevant:
- MapDeserializer has a specialization for deserialize_seq and deserialize_tuple, but
  only MapRefDeserializer::deserialize_any is used by the code which is almost the same
- MapDeserializer checks that map was consumed after visit_map, but MapRefDeserializer
  does not. Actually, each derived implementation consumes map and each manual implementation
  also should consume it

Also, MapDeserializer already used when value deserialized from ContentRefDeserializer
directly and MapRefDeserializer was only used to deserialize Struct variants of enums.
There are no reasons why the behavior should be different in those two cases
2024-08-24 04:52:26 +05:00
Mingun da7fc795ee Fix deserialization of empty struct variant in untagged enums
SeqRefDeserializer::deserialize_any has a special condition for empty sequence, which
emits visit_unit. That condition assumes that type would be able to deserialized from
unit, but:
1) struct variants was never able to deserialize from it (they expect only visit_map or visit_seq)
2) tuple variants even with zero fields expect visit_seq only. The suggestion to accept visit_unit
   instead was rejected in #2520

Fixes (2):
    newtype_enum::tuple0
    newtype_enum::empty_struct_from_seq
2024-08-24 04:52:26 +05:00
Mingun 4c5fec1363 Test special cases that reaches SeqRefDeserializer::deserialize_any len==0 condition
failures (2):
    newtype_enum::empty_struct_from_seq
    newtype_enum::tuple0
2024-08-24 04:52:25 +05:00
Mingun 6588b0ad37 Cover Content::Seq case in VariantRefDeserializer::struct_variant 2024-08-24 04:52:25 +05:00
Mingun 0093f74cfe Split test newtype_enum into four tests for each variant
(review this commit with "ignore whitespace changes" option on)
2024-08-24 04:52:25 +05:00
Mingun 171c6da57a Complete coverage of ContentRefDeserializer::deserialize_newtype_struct 2024-08-24 04:52:25 +05:00
Mingun 2dddc7796d Cover ContentRefDeserializer::deserialize_option 2024-08-24 04:52:25 +05:00
Mingun 8514f4119a Remove unnecessary generics 2024-08-24 04:52:24 +05:00
Mingun 98fb7d94aa Move all untagged enum tests (except flatten) into a dedicated module
Moved and renamed:
From test_annotations
- test_expecting_message_untagged_tagged_enum                  => expecting_message
- flatten::enum_::untagged::straitforward                      => contains_flatten

From test_macros
- test_untagged_newtype_struct                                 => newtype_struct
- test_untagged_enum                                           => complex
- test_untagged_enum_with_flattened_integer_key                => contains_flatten_with_integer_key
- test_enum_in_untagged_enum                                   => newtype_enum
- test_untagged_bytes                                          => string_and_bytes
- test_untagged_newtype_variant_containing_unit_struct_not_map => newtype_unit_and_empty_map
2024-08-24 04:52:22 +05:00
David Tolnay 31ca16d9bc Merge pull request #2804 from Mingun/adjacently-tagged-tests
Consolidate and add new tests of adjacently tagged enums
2024-08-23 14:12:41 -07:00
Mingun 9f72ce695e Struct: add tests for deserialization from sequence
(review this commit with "ignore whitespace changes" option on)
2024-08-16 22:51:42 +05:00
Mingun c383e4f953 Tuple: add tests for deserialization from sequence
(review this commit with "ignore whitespace changes" option on)
2024-08-16 22:51:16 +05:00
Mingun a94d8750fb Newtype: move up the test with tag only 2024-08-16 22:51:13 +05:00
Mingun b0d651be40 Newtype: add tests for deserialization from sequence
(review this commit with "ignore whitespace changes" option on)
2024-08-16 22:51:13 +05:00
Mingun d5a9c11b5c No need to test integer and byte array field names, they already tested for Unit case
There is no difference what variant is deserialized so we can test only one kind of variant
2024-08-16 22:51:10 +05:00
Mingun 6bfe1c435a Unit: add tests for deserialization from bytes tag and content fields 2024-08-16 22:43:15 +05:00
Mingun a02da49b87 Unit: add tests for deserialization from integer tag and content fields 2024-08-16 22:43:15 +05:00
Mingun 29dc6c3367 Unit: add tests for deserialization from sequence
(review this commit with "ignore whitespace changes" option on)
2024-08-16 22:38:02 +05:00
Mingun a7f0bab078 Document fields in internal structs used to deserialize adjacently tagged enums 2024-08-16 21:39:55 +05:00
Mingun 3dc6829303 Integrate bytes test into struct_ test
`newtype` test also integrates test with `Bytes` tag, so be like

Removed the first assert_tokens because it is the same as the first assert in the merged method
2024-08-16 21:39:53 +05:00
Mingun 42e63ff942 Reuse AdjacentlyTagged enum in bytes test
Change 0i32 to 1u8 so the test can be merged with the previous in the next commit
2024-08-16 21:38:23 +05:00
Mingun df07751e6f Group newtype and newtype_with_newtype tests 2024-08-16 21:36:56 +05:00
Mingun 5445f1741b Reuse AdjacentlyTagged enum in newtype_with_newtype test 2024-08-16 21:36:56 +05:00
Mingun 9d0f811221 Remove unnecessary generic 2024-08-16 21:36:56 +05:00
Mingun 36b9a859c4 Test deserialization of the serialized unit format for adjacently tagged enum 2024-08-16 21:36:55 +05:00
Mingun 59628d1712 Create only one value for all checks 2024-08-16 21:36:55 +05:00
Mingun bee7470715 Split test test_adjacently_tagged_enum into four tests for each variant
(review this commit with "ignore whitespace changes" option on)
2024-08-16 21:36:55 +05:00
Mingun 5a359e10f4 Group Newtype variant checks in test_adjacently_tagged_enum together 2024-08-16 21:36:55 +05:00
Mingun 5e37ade519 Move all adjacently tagged enum tests (except flatten) into a dedicated module
Moved and renamed:
From test_annotatons
- test_adjacently_tagged_enum_bytes               => bytes
- flatten::enum_::adjacently_tagged::straitforward=> struct_with_flatten
- test_expecting_message_adjacently_tagged_enum   => expecting_message
- test_partially_untagged_adjacently_tagged_enum  => partially_untagged

From test_macros
- test_adjacently_tagged_newtype_struct           => newtype_with_newtype
- test_adjacently_tagged_enum
- test_adjacently_tagged_enum_deny_unknown_fields => deny_unknown_fields
2024-08-16 21:36:52 +05:00
David Tolnay 1a9ffdbd0c Merge pull request #2788 from Mingun/internally-tagged-enum-tests
Consolidate and add new tests of internally tagged enums
2024-08-15 14:42:28 -07:00
Mingun 2adb0e99b0 Add additional checks for unit and newtype_unit tests
- check that unit variant can be deserialized from a map
- check that newtype variant with unit can be deserialized from a struct
2024-08-15 23:39:14 +05:00
Mingun 71ed1f2f12 Add tests for special and general cases for internally tagged enums
Special case is the tag field first (so the enum variant are known after reading the first entry from map).
General case is the tag field not the first (so we need to buffer entries until we found an entry with tag)
2024-08-15 23:39:13 +05:00
Mingun 47954502af Add tests with borrowed strings for the tag field name and tag value 2024-08-15 23:39:13 +05:00
Mingun 4987fd15f7 Convert newtype_enum and struct_enum tests into modules
Separate testing each variant kind of enum (unit, newtype, tuple, struct) results
in more specific information if that checks fail

(review this commit with "ignore whitespace changes" option on)
2024-08-15 23:39:12 +05:00
Mingun 8bfe0d0ac0 Move and rename tests:
- newtype_variant_containing_unit                   -> newtype_unit
- newtype_variant_containing_unit_struct            -> newtype_unit_struct
- newtype_variant_containing_externally_tagged_enum -> newtype_enum
- struct_variant_containing_unit_variant            -> struct_enum
2024-08-15 23:39:12 +05:00
Mingun 7c0e6bd18f Reuse type in struct_variant_containing_unit_variant 2024-08-15 23:39:11 +05:00
Mingun 41b9c33c2b Reuse type in newtype_variant_containing_externally_tagged_enum 2024-08-15 23:39:11 +05:00
Mingun 28a775db22 Share externally tagged enum Enum between all tests and reuse in struct_variant_containing_unit_variant 2024-08-15 23:39:10 +05:00
Mingun e999600f8f Rename externally tagged enum Inner to Enum 2024-08-15 23:39:10 +05:00
Mingun d3492d8d36 Reuse type in newtype_variant_containing_unit_struct 2024-08-15 23:39:09 +05:00
Mingun 48de0c51b0 Share unit struct Unit between all tests 2024-08-15 23:39:09 +05:00
Mingun 93bda5f1dc Rename unit struct to a generic name: Info->Unit 2024-08-15 23:39:09 +05:00
Mingun 2d75ef6b30 Reuse type in newtype_variant_containing_unit 2024-08-15 23:39:07 +05:00
Mingun f97160f715 Reuse type in unit_variant_with_unknown_fields and add test for sequence 2024-08-15 23:39:06 +05:00
Mingun eb59c776ca Use name "tag" to refer to tag field 2024-08-15 23:39:06 +05:00
Mingun 9128201c78 Use the same order of derives 2024-08-15 23:39:05 +05:00
Mingun 2cbfd37072 Move all other internally tagged enum tests into a dedicated file
Moved all except flattened tests:
- test_internally_tagged_enum_with_skipped_conflict    => with_skipped_conflict
- test_internally_tagged_enum_new_type_with_unit       => newtype_variant_containing_unit
- test_internally_tagged_unit_enum_with_unknown_fields => unit_variant_with_unknown_fields
- test_expecting_message_internally_tagged_enum        => expecting_message
- flatten::enum_::internally_tagged::straightforward   => containing_flatten
2024-08-15 23:39:05 +05:00
Mingun 0939214c51 Move internally tagged enum tests into a dedicated file
Ctrl+X, Ctrl+V
2024-08-15 23:34:01 +05:00
Mingun 8c60f5aea7 Reorder enum variants and tests to canonical order (Unit, Newtype, Tuple, Struct) 2024-08-15 23:34:00 +05:00
Mingun da0ed4021d Give meaningful names to enum variants 2024-08-15 23:34:00 +05:00
Mingun 99f905403b Move all internally tagged enum tests of test_macros into a dedicated module
Moved:
- test_internally_tagged_enum_with_untagged_variant             => untagged_variant
- test_internally_tagged_bytes                                  => string_and_bytes mod
- test_internally_tagged_struct_variant_containing_unit_variant => struct_variant_containing_unit_variant
- test_internally_tagged_borrow                                 => borrow
- test_enum_in_internally_tagged_enum                           => newtype_variant_containing_externally_tagged_enum
- test_internally_tagged_newtype_variant_containing_unit_struct => newtype_variant_containing_unit_struct

(review this commit with "ignore whitespace changes" option on)
2024-08-15 23:34:00 +05:00
Mingun aa0654332d Convert test_internally_tagged_enum into module
(review this commit with "ignore whitespace changes" option on)
2024-08-15 23:33:59 +05:00
David Tolnay af376c22c3 Merge pull request #2803 from jonhoo/mv-flatten-tests
Group flatten tests
2024-08-15 10:53:49 -07:00
Jon Gjengset 477eb7b70e Group flatten tests 2024-08-15 18:28:49 +02:00
David Tolnay 026e91a68c Release 1.0.208 2024-08-15 08:41:27 -07:00
David Tolnay bfbedac919 Merge pull request #2802 from jonhoo/flatten-unit-struct
Support (de-)serializing flattened unit struct
2024-08-15 08:40:11 -07:00
Jon Gjengset 4036ff88ed Support (de-)serializing flattened unit struct
Fixes #2801.
2024-08-15 15:50:54 +02:00
David Tolnay 1b4da41f97 Release 1.0.207 2024-08-12 13:06:46 -07:00
David Tolnay f61d452814 Touch up PR 2795 2024-08-12 13:03:10 -07:00
David Tolnay f9866097a0 Merge pull request #2795 from Mingun/has-flatten-rework
`has_flatten` rework
2024-08-12 13:02:58 -07:00
Mingun 77a6a9d4e1 Take into account only not skipped flatten fields when choose serialization form
Consequence: `FlattenSkipSerializing`
- uses `serialize_struct` instead of `serialize_map`
2024-08-11 20:12:09 +05:00
Mingun 547d843cca Remove dead code - serialize_struct_as_map always called when cattrs.has_flatten()==true 2024-08-11 20:12:08 +05:00
Mingun 005cb84593 Fail with an understandable message is number of fields for serialization is too many 2024-08-11 20:12:07 +05:00
Mingun fd5b5e9aa5 Correctly calculate has_flatten attribute in all cases for deserialization
Consequence: `FlattenSkipDeserializing[DenyUnknown]`
- does not collect data in Field, because do not read them anyway
- gets `deserialize_in_place` method
- gets ability to deserialize from sequence (visit_seq method)
- uses `deserialize_struct` instead of `deserialize_map`
2024-08-11 20:01:33 +05:00
Mingun 0647a7c1fe Fix creating and filling a collections that was not read 2024-08-11 20:01:00 +05:00
David Tolnay 85c73ef8de Release 1.0.206 2024-08-11 00:08:37 -07:00
David Tolnay 5ba1796a7e Resolve doc_markdown pedantic lint on regression test function
warning: you should put bare URLs between `<`/`>` or make a proper Markdown link
        --> test_suite/tests/test_annotations.rs:2383:25
         |
    2383 | /// Regression test for https://github.com/serde-rs/serde/issues/1904
         |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `<https://github.com/serde-rs/serde/issues/1904>`
         |
         = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
         = note: `-W clippy::doc-markdown` implied by `-W clippy::pedantic`
         = help: to override `-W clippy::pedantic` add `#[allow(clippy::doc_markdown)]`
2024-08-11 00:08:16 -07:00
David Tolnay e52b7b380f Touch up PR 2567 2024-08-11 00:05:56 -07:00
David Tolnay 84c7419652 Merge pull request #2794 from dtolnay/neverread
Temporarily ignore collection_is_never_read on FlattenSkipDeserializing
2024-08-11 00:05:31 -07:00
David Tolnay 536221b1f9 Temporarily ignore collection_is_never_read on FlattenSkipDeserializing
error: collection is never read
       --> test_suite/tests/test_gen.rs:723:25
        |
    723 |     #[derive(Serialize, Deserialize)]
        |                         ^^^^^^^^^^^
        |
        = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collection_is_never_read
    note: the lint level is defined here
       --> test_suite/tests/test_gen.rs:23:9
        |
    23  | #![deny(clippy::collection_is_never_read)]
        |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        = note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info)
2024-08-10 23:59:23 -07:00
David Tolnay fc55ac70d3 Merge pull request #2567 from Mingun/fix-2565
Correctly process flatten fields in enum variants
2024-08-10 23:28:22 -07:00
Mingun 2afe5b4ef9 Add regression test for issue #2792 2024-08-10 21:29:39 +05:00
Mingun b4ec2595c9 Correctly process flatten fields in enum variants
- Fix incorrect deserialization of variants that doesn't contain flatten field when other contains
- Fix a panic when deriving `Deserialize` for an enum with tuple and struct with flatten field

Fixes (2):
    regression::issue2565::simple_variant
    regression::issue1904 (compilation)
2024-08-09 19:59:23 +05:00
Mingun c3ac7b675a Add regression test for issue #1904
Currently panics in derive:

error: proc-macro derive panicked
    --> test_suite\tests\test_annotations.rs:2386:25
     |
2386 |     #[derive(Serialize, Deserialize, PartialEq, Debug)]
     |                         ^^^^^^^^^^^
     |
     = help: message: assertion failed: !cattrs.has_flatten()

error: proc-macro derive panicked
  --> test_suite\tests\regression\issue1904.rs:57:10
   |
57 | #[derive(Deserialize)]
   |          ^^^^^^^^^^^
   |
   = help: message: assertion failed: !cattrs.has_flatten()

error: proc-macro derive panicked
  --> test_suite\tests\regression\issue1904.rs:47:10
   |
47 | #[derive(Deserialize)]
   |          ^^^^^^^^^^^
   |
   = help: message: assertion failed: !cattrs.has_flatten()

error: proc-macro derive panicked
  --> test_suite\tests\regression\issue1904.rs:37:10
   |
37 | #[derive(Deserialize)]
   |          ^^^^^^^^^^^
   |
   = help: message: assertion failed: !cattrs.has_flatten()

error: proc-macro derive panicked
  --> test_suite\tests\regression\issue1904.rs:27:10
   |
27 | #[derive(Deserialize)]
   |          ^^^^^^^^^^^
   |
   = help: message: assertion failed: !cattrs.has_flatten()

error: proc-macro derive panicked
  --> test_suite\tests\regression\issue1904.rs:16:10
   |
16 | #[derive(Deserialize)]
   |          ^^^^^^^^^^^
   |
   = help: message: assertion failed: !cattrs.has_flatten()

error: proc-macro derive panicked
 --> test_suite\tests\regression\issue1904.rs:7:10
  |
7 | #[derive(Deserialize)]
  |          ^^^^^^^^^^^
  |
  = help: message: assertion failed: !cattrs.has_flatten()
2024-08-09 19:55:56 +05:00
Mingun 24614e44bf Add regression test for issue #2565
failures (1):
    regression::issue2565::simple_variant
2024-08-09 19:53:14 +05:00
26 changed files with 3886 additions and 2657 deletions
+5
View File
@@ -21,6 +21,11 @@ jobs:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
- run: cd test_suite && cargo test --features unstable
- uses: actions/upload-artifact@v4
if: always()
with:
name: Cargo.lock
path: Cargo.lock
windows:
name: Test suite (windows)
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "serde"
version = "1.0.205"
version = "1.0.210"
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>", "David Tolnay <dtolnay@gmail.com>"]
build = "build.rs"
categories = ["encoding", "no-std", "no-std::no-alloc"]
@@ -37,7 +37,7 @@ rustdoc-args = ["--generate-link-to-definition"]
# is compatible with exactly one serde release because the generated code
# involves nonpublic APIs which are not bound by semver.
[target.'cfg(any())'.dependencies]
serde_derive = { version = "=1.0.205", path = "../serde_derive" }
serde_derive = { version = "=1.0.210", path = "../serde_derive" }
### FEATURES #################################################################
+14
View File
@@ -15,6 +15,8 @@ fn main() {
if minor >= 77 {
println!("cargo:rustc-check-cfg=cfg(no_core_cstr)");
println!("cargo:rustc-check-cfg=cfg(no_core_error)");
println!("cargo:rustc-check-cfg=cfg(no_core_net)");
println!("cargo:rustc-check-cfg=cfg(no_core_num_saturating)");
println!("cargo:rustc-check-cfg=cfg(no_core_try_from)");
println!("cargo:rustc-check-cfg=cfg(no_diagnostic_namespace)");
@@ -86,11 +88,23 @@ fn main() {
println!("cargo:rustc-cfg=no_core_num_saturating");
}
// Support for core::net stabilized in Rust 1.77.
// https://blog.rust-lang.org/2024/03/21/Rust-1.77.0.html
if minor < 77 {
println!("cargo:rustc-cfg=no_core_net");
}
// Support for the `#[diagnostic]` tool attribute namespace
// https://blog.rust-lang.org/2024/05/02/Rust-1.78.0.html#diagnostic-attributes
if minor < 78 {
println!("cargo:rustc-cfg=no_diagnostic_namespace");
}
// The Error trait became available in core in 1.81.
// https://blog.rust-lang.org/2024/09/05/Rust-1.81.0.html#coreerrorerror
if minor < 81 {
println!("cargo:rustc-cfg=no_core_error");
}
}
fn rustc_minor_version() -> Option<u32> {
+16 -30
View File
@@ -1583,12 +1583,9 @@ map_impl! {
////////////////////////////////////////////////////////////////////////////////
#[cfg(any(feature = "std", not(no_core_net)))]
macro_rules! parse_ip_impl {
(
$(#[$attr:meta])*
$ty:ty, $expecting:expr, $size:tt
) => {
$(#[$attr])*
($ty:ty, $expecting:expr, $size:tt) => {
impl<'de> Deserialize<'de> for $ty {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
@@ -1604,7 +1601,7 @@ macro_rules! parse_ip_impl {
};
}
#[cfg(feature = "std")]
#[cfg(any(feature = "std", not(no_core_net)))]
macro_rules! variant_identifier {
(
$name_kind:ident ($($variant:ident; $bytes:expr; $index:expr),*)
@@ -1679,7 +1676,7 @@ macro_rules! variant_identifier {
}
}
#[cfg(feature = "std")]
#[cfg(any(feature = "std", not(no_core_net)))]
macro_rules! deserialize_enum {
(
$name:ident $name_kind:ident ($($variant:ident; $bytes:expr; $index:expr),*)
@@ -1716,8 +1713,7 @@ macro_rules! deserialize_enum {
}
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
#[cfg(any(feature = "std", not(no_core_net)))]
impl<'de> Deserialize<'de> for net::IpAddr {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
@@ -1736,25 +1732,18 @@ impl<'de> Deserialize<'de> for net::IpAddr {
}
}
parse_ip_impl! {
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
net::Ipv4Addr, "IPv4 address", 4
}
#[cfg(any(feature = "std", not(no_core_net)))]
parse_ip_impl!(net::Ipv4Addr, "IPv4 address", 4);
parse_ip_impl! {
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
net::Ipv6Addr, "IPv6 address", 16
}
#[cfg(any(feature = "std", not(no_core_net)))]
parse_ip_impl!(net::Ipv6Addr, "IPv6 address", 16);
#[cfg(any(feature = "std", not(no_core_net)))]
macro_rules! parse_socket_impl {
(
$(#[$attr:meta])*
$ty:ty, $expecting:tt,
$new:expr,
) => {
$(#[$attr])*
impl<'de> Deserialize<'de> for $ty {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
@@ -1770,8 +1759,7 @@ macro_rules! parse_socket_impl {
};
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
#[cfg(any(feature = "std", not(no_core_net)))]
impl<'de> Deserialize<'de> for net::SocketAddr {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
@@ -1790,16 +1778,14 @@ impl<'de> Deserialize<'de> for net::SocketAddr {
}
}
#[cfg(any(feature = "std", not(no_core_net)))]
parse_socket_impl! {
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
net::SocketAddrV4, "IPv4 socket address",
|(ip, port)| net::SocketAddrV4::new(ip, port),
}
#[cfg(any(feature = "std", not(no_core_net)))]
parse_socket_impl! {
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
net::SocketAddrV6, "IPv6 socket address",
|(ip, port)| net::SocketAddrV6::new(ip, port, 0, 0),
}
@@ -3160,13 +3146,13 @@ atomic_impl! {
AtomicU64 "64"
}
#[cfg(feature = "std")]
#[cfg(any(feature = "std", not(no_core_net)))]
struct FromStrVisitor<T> {
expecting: &'static str,
ty: PhantomData<T>,
}
#[cfg(feature = "std")]
#[cfg(any(feature = "std", not(no_core_net)))]
impl<T> FromStrVisitor<T> {
fn new(expecting: &'static str) -> Self {
FromStrVisitor {
@@ -3176,7 +3162,7 @@ impl<T> FromStrVisitor<T> {
}
}
#[cfg(feature = "std")]
#[cfg(any(feature = "std", not(no_core_net)))]
impl<'de, T> Visitor<'de> for FromStrVisitor<T>
where
T: str::FromStr,
+4 -5
View File
@@ -118,17 +118,16 @@ use crate::lib::*;
pub mod value;
mod format;
mod ignored_any;
mod impls;
pub(crate) mod size_hint;
pub use self::ignored_any::IgnoredAny;
#[cfg(not(any(feature = "std", feature = "unstable")))]
#[cfg(all(not(feature = "std"), no_core_error))]
#[doc(no_inline)]
pub use crate::std_error::Error as StdError;
#[cfg(all(feature = "unstable", not(feature = "std")))]
#[cfg(not(any(feature = "std", no_core_error)))]
#[doc(no_inline)]
pub use core::error::Error as StdError;
#[cfg(feature = "std")]
@@ -1374,7 +1373,7 @@ pub trait Visitor<'de>: Sized {
E: Error,
{
let mut buf = [0u8; 58];
let mut writer = format::Buf::new(&mut buf);
let mut writer = crate::format::Buf::new(&mut buf);
fmt::Write::write_fmt(&mut writer, format_args!("integer `{}` as i128", v)).unwrap();
Err(Error::invalid_type(
Unexpected::Other(writer.as_str()),
@@ -1436,7 +1435,7 @@ pub trait Visitor<'de>: Sized {
E: Error,
{
let mut buf = [0u8; 57];
let mut writer = format::Buf::new(&mut buf);
let mut writer = crate::format::Buf::new(&mut buf);
fmt::Write::write_fmt(&mut writer, format_args!("integer `{}` as u128", v)).unwrap();
Err(Error::invalid_type(
Unexpected::Other(writer.as_str()),
+10 -3
View File
@@ -95,7 +95,7 @@
////////////////////////////////////////////////////////////////////////////////
// Serde types in rustdoc of other crates get linked to here.
#![doc(html_root_url = "https://docs.rs/serde/1.0.205")]
#![doc(html_root_url = "https://docs.rs/serde/1.0.210")]
// Support using Serde without the standard library!
#![cfg_attr(not(feature = "std"), no_std)]
// Show which crate feature enables conditionally compiled APIs in documentation.
@@ -238,8 +238,13 @@ mod lib {
#[cfg(feature = "std")]
pub use std::ffi::CString;
#[cfg(all(not(no_core_net), not(feature = "std")))]
pub use self::core::net;
#[cfg(feature = "std")]
pub use std::{error, net};
pub use std::net;
#[cfg(feature = "std")]
pub use std::error;
#[cfg(feature = "std")]
pub use std::collections::{HashMap, HashSet};
@@ -305,6 +310,8 @@ mod integer128;
pub mod de;
pub mod ser;
mod format;
#[doc(inline)]
pub use crate::de::{Deserialize, Deserializer};
#[doc(inline)]
@@ -318,7 +325,7 @@ pub mod __private;
#[path = "de/seed.rs"]
mod seed;
#[cfg(not(any(feature = "std", feature = "unstable")))]
#[cfg(all(not(feature = "std"), no_core_error))]
mod std_error;
// Re-export #[derive(Serialize, Deserialize)].
+58 -162
View File
@@ -904,7 +904,9 @@ mod content {
/// Not public API.
pub struct TagOrContentFieldVisitor {
/// Name of the tag field of the adjacently tagged enum
pub tag: &'static str,
/// Name of the content field of the adjacently tagged enum
pub content: &'static str,
}
@@ -979,7 +981,9 @@ mod content {
/// Not public API.
pub struct TagContentOtherFieldVisitor {
/// Name of the tag field of the adjacently tagged enum
pub tag: &'static str,
/// Name of the content field of the adjacently tagged enum
pub content: &'static str,
}
@@ -1894,10 +1898,19 @@ mod content {
where
V: Visitor<'de>,
{
// Covered by tests/test_enum_untagged.rs
// with_optional_field::*
match *self.content {
Content::None => visitor.visit_none(),
Content::Some(ref v) => visitor.visit_some(ContentRefDeserializer::new(v)),
Content::Unit => visitor.visit_unit(),
// This case is to support data formats which do not encode an
// indication whether a value is optional. An example of such a
// format is JSON, and a counterexample is RON. When requesting
// `deserialize_any` in JSON, the data format never performs
// `Visitor::visit_some` but we still must be able to
// deserialize the resulting Content into data structures with
// optional fields.
_ => visitor.visit_some(self),
}
}
@@ -1927,10 +1940,21 @@ mod content {
where
V: Visitor<'de>,
{
// Covered by tests/test_enum_untagged.rs
// newtype_struct
match *self.content {
Content::Newtype(ref v) => {
visitor.visit_newtype_struct(ContentRefDeserializer::new(v))
}
// This case is to support data formats that encode newtype
// structs and their underlying data the same, with no
// indication whether a newtype wrapper was present. For example
// JSON does this, while RON does not. In RON a newtype's name
// is included in the serialized representation and it knows to
// call `Visitor::visit_newtype_struct` from `deserialize_any`.
// JSON's `deserialize_any` never calls `visit_newtype_struct`
// but in this code we still must be able to deserialize the
// resulting Content into newtypes.
_ => visitor.visit_newtype_struct(self),
}
}
@@ -2135,6 +2159,10 @@ mod content {
fn unit_variant(self) -> Result<(), E> {
match self.value {
Some(value) => de::Deserialize::deserialize(ContentRefDeserializer::new(value)),
// Covered by tests/test_annotations.rs
// test_partially_untagged_adjacently_tagged_enum
// Covered by tests/test_enum_untagged.rs
// newtype_enum::unit
None => Ok(()),
}
}
@@ -2144,6 +2172,11 @@ mod content {
T: de::DeserializeSeed<'de>,
{
match self.value {
// Covered by tests/test_annotations.rs
// test_partially_untagged_enum_desugared
// test_partially_untagged_enum_generic
// Covered by tests/test_enum_untagged.rs
// newtype_enum::newtype
Some(value) => seed.deserialize(ContentRefDeserializer::new(value)),
None => Err(de::Error::invalid_type(
de::Unexpected::UnitVariant,
@@ -2157,9 +2190,13 @@ mod content {
V: de::Visitor<'de>,
{
match self.value {
Some(Content::Seq(v)) => {
de::Deserializer::deserialize_any(SeqRefDeserializer::new(v), visitor)
}
// Covered by tests/test_annotations.rs
// test_partially_untagged_enum
// test_partially_untagged_enum_desugared
// Covered by tests/test_enum_untagged.rs
// newtype_enum::tuple0
// newtype_enum::tuple2
Some(Content::Seq(v)) => visit_content_seq_ref(v, visitor),
Some(other) => Err(de::Error::invalid_type(
other.unexpected(),
&"tuple variant",
@@ -2180,12 +2217,13 @@ mod content {
V: de::Visitor<'de>,
{
match self.value {
Some(Content::Map(v)) => {
de::Deserializer::deserialize_any(MapRefDeserializer::new(v), visitor)
}
Some(Content::Seq(v)) => {
de::Deserializer::deserialize_any(SeqRefDeserializer::new(v), visitor)
}
// Covered by tests/test_enum_untagged.rs
// newtype_enum::struct_from_map
Some(Content::Map(v)) => visit_content_map_ref(v, visitor),
// Covered by tests/test_enum_untagged.rs
// newtype_enum::struct_from_seq
// newtype_enum::empty_struct_from_seq
Some(Content::Seq(v)) => visit_content_seq_ref(v, visitor),
Some(other) => Err(de::Error::invalid_type(
other.unexpected(),
&"struct variant",
@@ -2198,158 +2236,6 @@ mod content {
}
}
struct SeqRefDeserializer<'a, 'de: 'a, E>
where
E: de::Error,
{
iter: <&'a [Content<'de>] as IntoIterator>::IntoIter,
err: PhantomData<E>,
}
impl<'a, 'de, E> SeqRefDeserializer<'a, 'de, E>
where
E: de::Error,
{
fn new(slice: &'a [Content<'de>]) -> Self {
SeqRefDeserializer {
iter: slice.iter(),
err: PhantomData,
}
}
}
impl<'de, 'a, E> de::Deserializer<'de> for SeqRefDeserializer<'a, 'de, E>
where
E: de::Error,
{
type Error = E;
#[inline]
fn deserialize_any<V>(mut self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
let len = self.iter.len();
if len == 0 {
visitor.visit_unit()
} else {
let ret = tri!(visitor.visit_seq(&mut self));
let remaining = self.iter.len();
if remaining == 0 {
Ok(ret)
} else {
Err(de::Error::invalid_length(len, &"fewer elements in array"))
}
}
}
forward_to_deserialize_any! {
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
bytes byte_buf option unit unit_struct newtype_struct seq tuple
tuple_struct map struct enum identifier ignored_any
}
}
impl<'de, 'a, E> de::SeqAccess<'de> for SeqRefDeserializer<'a, 'de, E>
where
E: de::Error,
{
type Error = E;
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
where
T: de::DeserializeSeed<'de>,
{
match self.iter.next() {
Some(value) => seed
.deserialize(ContentRefDeserializer::new(value))
.map(Some),
None => Ok(None),
}
}
fn size_hint(&self) -> Option<usize> {
size_hint::from_bounds(&self.iter)
}
}
struct MapRefDeserializer<'a, 'de: 'a, E>
where
E: de::Error,
{
iter: <&'a [(Content<'de>, Content<'de>)] as IntoIterator>::IntoIter,
value: Option<&'a Content<'de>>,
err: PhantomData<E>,
}
impl<'a, 'de, E> MapRefDeserializer<'a, 'de, E>
where
E: de::Error,
{
fn new(map: &'a [(Content<'de>, Content<'de>)]) -> Self {
MapRefDeserializer {
iter: map.iter(),
value: None,
err: PhantomData,
}
}
}
impl<'de, 'a, E> de::MapAccess<'de> for MapRefDeserializer<'a, 'de, E>
where
E: de::Error,
{
type Error = E;
fn next_key_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
where
T: de::DeserializeSeed<'de>,
{
match self.iter.next() {
Some((key, value)) => {
self.value = Some(value);
seed.deserialize(ContentRefDeserializer::new(key)).map(Some)
}
None => Ok(None),
}
}
fn next_value_seed<T>(&mut self, seed: T) -> Result<T::Value, Self::Error>
where
T: de::DeserializeSeed<'de>,
{
match self.value.take() {
Some(value) => seed.deserialize(ContentRefDeserializer::new(value)),
None => Err(de::Error::custom("value is missing")),
}
}
fn size_hint(&self) -> Option<usize> {
size_hint::from_bounds(&self.iter)
}
}
impl<'de, 'a, E> de::Deserializer<'de> for MapRefDeserializer<'a, 'de, E>
where
E: de::Error,
{
type Error = E;
#[inline]
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
visitor.visit_map(self)
}
forward_to_deserialize_any! {
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
bytes byte_buf option unit unit_struct newtype_struct seq tuple
tuple_struct map struct enum identifier ignored_any
}
}
impl<'de, E> de::IntoDeserializer<'de, E> for ContentDeserializer<'de, E>
where
E: de::Error,
@@ -2710,6 +2596,17 @@ where
visitor.visit_unit()
}
fn deserialize_unit_struct<V>(
self,
_name: &'static str,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_unit()
}
fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
@@ -2734,7 +2631,6 @@ where
deserialize_string()
deserialize_bytes()
deserialize_byte_buf()
deserialize_unit_struct(&'static str)
deserialize_seq()
deserialize_tuple(usize)
deserialize_tuple_struct(&'static str, usize)
+1 -5
View File
@@ -51,8 +51,6 @@ enum Unsupported {
String,
ByteArray,
Optional,
#[cfg(any(feature = "std", feature = "alloc"))]
UnitStruct,
Sequence,
Tuple,
TupleStruct,
@@ -69,8 +67,6 @@ impl Display for Unsupported {
Unsupported::String => formatter.write_str("a string"),
Unsupported::ByteArray => formatter.write_str("a byte array"),
Unsupported::Optional => formatter.write_str("an optional"),
#[cfg(any(feature = "std", feature = "alloc"))]
Unsupported::UnitStruct => formatter.write_str("unit struct"),
Unsupported::Sequence => formatter.write_str("a sequence"),
Unsupported::Tuple => formatter.write_str("a tuple"),
Unsupported::TupleStruct => formatter.write_str("a tuple struct"),
@@ -1092,7 +1088,7 @@ where
}
fn serialize_unit_struct(self, _: &'static str) -> Result<Self::Ok, Self::Error> {
Err(Self::bad_type(Unsupported::UnitStruct))
Ok(())
}
fn serialize_unit_variant(
+13 -29
View File
@@ -773,28 +773,17 @@ impl Serialize for SystemTime {
/// statically known to never have more than a constant `MAX_LEN` bytes.
///
/// Panics if the `Display` impl tries to write more than `MAX_LEN` bytes.
#[cfg(feature = "std")]
#[cfg(any(feature = "std", not(no_core_net)))]
macro_rules! serialize_display_bounded_length {
($value:expr, $max:expr, $serializer:expr) => {{
let mut buffer = [0u8; $max];
let remaining_len = {
let mut remaining = &mut buffer[..];
write!(remaining, "{}", $value).unwrap();
remaining.len()
};
let written_len = buffer.len() - remaining_len;
let written = &buffer[..written_len];
// write! only provides fmt::Formatter to Display implementations, which
// has methods write_str and write_char but no method to write arbitrary
// bytes. Therefore `written` must be valid UTF-8.
let written_str = str::from_utf8(written).expect("must be valid UTF-8");
$serializer.serialize_str(written_str)
let mut writer = crate::format::Buf::new(&mut buffer);
write!(&mut writer, "{}", $value).unwrap();
$serializer.serialize_str(writer.as_str())
}};
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
#[cfg(any(feature = "std", not(no_core_net)))]
impl Serialize for net::IpAddr {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
@@ -818,7 +807,7 @@ impl Serialize for net::IpAddr {
}
}
#[cfg(feature = "std")]
#[cfg(any(feature = "std", not(no_core_net)))]
const DEC_DIGITS_LUT: &[u8] = b"\
0001020304050607080910111213141516171819\
2021222324252627282930313233343536373839\
@@ -826,7 +815,7 @@ const DEC_DIGITS_LUT: &[u8] = b"\
6061626364656667686970717273747576777879\
8081828384858687888990919293949596979899";
#[cfg(feature = "std")]
#[cfg(any(feature = "std", not(no_core_net)))]
#[inline]
fn format_u8(mut n: u8, out: &mut [u8]) -> usize {
if n >= 100 {
@@ -847,7 +836,7 @@ fn format_u8(mut n: u8, out: &mut [u8]) -> usize {
}
}
#[cfg(feature = "std")]
#[cfg(any(feature = "std", not(no_core_net)))]
#[test]
fn test_format_u8() {
let mut i = 0u8;
@@ -864,8 +853,7 @@ fn test_format_u8() {
}
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
#[cfg(any(feature = "std", not(no_core_net)))]
impl Serialize for net::Ipv4Addr {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
@@ -889,8 +877,7 @@ impl Serialize for net::Ipv4Addr {
}
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
#[cfg(any(feature = "std", not(no_core_net)))]
impl Serialize for net::Ipv6Addr {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
@@ -906,8 +893,7 @@ impl Serialize for net::Ipv6Addr {
}
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
#[cfg(any(feature = "std", not(no_core_net)))]
impl Serialize for net::SocketAddr {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
@@ -931,8 +917,7 @@ impl Serialize for net::SocketAddr {
}
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
#[cfg(any(feature = "std", not(no_core_net)))]
impl Serialize for net::SocketAddrV4 {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
@@ -948,8 +933,7 @@ impl Serialize for net::SocketAddrV4 {
}
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
#[cfg(any(feature = "std", not(no_core_net)))]
impl Serialize for net::SocketAddrV6 {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
+2 -2
View File
@@ -115,10 +115,10 @@ mod impossible;
pub use self::impossible::Impossible;
#[cfg(not(any(feature = "std", feature = "unstable")))]
#[cfg(all(not(feature = "std"), no_core_error))]
#[doc(no_inline)]
pub use crate::std_error::Error as StdError;
#[cfg(all(feature = "unstable", not(feature = "std")))]
#[cfg(not(any(feature = "std", no_core_error)))]
#[doc(no_inline)]
pub use core::error::Error as StdError;
#[cfg(feature = "std")]
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "serde_derive"
version = "1.0.205"
version = "1.0.210"
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>", "David Tolnay <dtolnay@gmail.com>"]
categories = ["no-std", "no-std::no-alloc"]
description = "Macros 1.1 implementation of #[derive(Serialize, Deserialize)]"
+49 -28
View File
@@ -461,7 +461,10 @@ fn deserialize_tuple(
cattrs: &attr::Container,
form: TupleForm,
) -> Fragment {
assert!(!cattrs.has_flatten());
assert!(
!has_flatten(fields),
"tuples and tuple variants cannot have flatten fields"
);
let field_count = fields
.iter()
@@ -579,7 +582,10 @@ fn deserialize_tuple_in_place(
fields: &[Field],
cattrs: &attr::Container,
) -> Fragment {
assert!(!cattrs.has_flatten());
assert!(
!has_flatten(fields),
"tuples and tuple variants cannot have flatten fields"
);
let field_count = fields
.iter()
@@ -958,13 +964,15 @@ fn deserialize_struct(
)
})
.collect();
let field_visitor = deserialize_field_identifier(&field_names_idents, cattrs);
let has_flatten = has_flatten(fields);
let field_visitor = deserialize_field_identifier(&field_names_idents, cattrs, has_flatten);
// untagged struct variants do not get a visit_seq method. The same applies to
// structs that only have a map representation.
let visit_seq = match form {
StructForm::Untagged(..) => None,
_ if cattrs.has_flatten() => None,
_ if has_flatten => None,
_ => {
let mut_seq = if field_names_idents.is_empty() {
quote!(_)
@@ -987,10 +995,16 @@ fn deserialize_struct(
})
}
};
let visit_map = Stmts(deserialize_map(&type_path, params, fields, cattrs));
let visit_map = Stmts(deserialize_map(
&type_path,
params,
fields,
cattrs,
has_flatten,
));
let visitor_seed = match form {
StructForm::ExternallyTagged(..) if cattrs.has_flatten() => Some(quote! {
StructForm::ExternallyTagged(..) if has_flatten => Some(quote! {
impl #de_impl_generics _serde::de::DeserializeSeed<#delife> for __Visitor #de_ty_generics #where_clause {
type Value = #this_type #ty_generics;
@@ -1005,7 +1019,7 @@ fn deserialize_struct(
_ => None,
};
let fields_stmt = if cattrs.has_flatten() {
let fields_stmt = if has_flatten {
None
} else {
let field_names = field_names_idents
@@ -1025,7 +1039,7 @@ fn deserialize_struct(
}
};
let dispatch = match form {
StructForm::Struct if cattrs.has_flatten() => quote! {
StructForm::Struct if has_flatten => quote! {
_serde::Deserializer::deserialize_map(__deserializer, #visitor_expr)
},
StructForm::Struct => {
@@ -1034,7 +1048,7 @@ fn deserialize_struct(
_serde::Deserializer::deserialize_struct(__deserializer, #type_name, FIELDS, #visitor_expr)
}
}
StructForm::ExternallyTagged(_) if cattrs.has_flatten() => quote! {
StructForm::ExternallyTagged(_) if has_flatten => quote! {
_serde::de::VariantAccess::newtype_variant_seed(__variant, #visitor_expr)
},
StructForm::ExternallyTagged(_) => quote! {
@@ -1091,7 +1105,7 @@ fn deserialize_struct_in_place(
) -> Option<Fragment> {
// for now we do not support in_place deserialization for structs that
// are represented as map.
if cattrs.has_flatten() {
if has_flatten(fields) {
return None;
}
@@ -1116,7 +1130,7 @@ fn deserialize_struct_in_place(
})
.collect();
let field_visitor = deserialize_field_identifier(&field_names_idents, cattrs);
let field_visitor = deserialize_field_identifier(&field_names_idents, cattrs, false);
let mut_seq = if field_names_idents.is_empty() {
quote!(_)
@@ -1210,10 +1224,7 @@ fn deserialize_homogeneous_enum(
}
}
fn prepare_enum_variant_enum(
variants: &[Variant],
cattrs: &attr::Container,
) -> (TokenStream, Stmts) {
fn prepare_enum_variant_enum(variants: &[Variant]) -> (TokenStream, Stmts) {
let mut deserialized_variants = variants
.iter()
.enumerate()
@@ -1247,7 +1258,7 @@ fn prepare_enum_variant_enum(
let variant_visitor = Stmts(deserialize_generated_identifier(
&variant_names_idents,
cattrs,
false, // variant identifiers do not depend on the presence of flatten fields
true,
None,
fallthrough,
@@ -1270,7 +1281,7 @@ fn deserialize_externally_tagged_enum(
let expecting = format!("enum {}", params.type_name());
let expecting = cattrs.expecting().unwrap_or(&expecting);
let (variants_stmt, variant_visitor) = prepare_enum_variant_enum(variants, cattrs);
let (variants_stmt, variant_visitor) = prepare_enum_variant_enum(variants);
// Match arms to extract a variant from a string
let variant_arms = variants
@@ -1355,7 +1366,7 @@ fn deserialize_internally_tagged_enum(
cattrs: &attr::Container,
tag: &str,
) -> Fragment {
let (variants_stmt, variant_visitor) = prepare_enum_variant_enum(variants, cattrs);
let (variants_stmt, variant_visitor) = prepare_enum_variant_enum(variants);
// Match arms to extract a variant from a string
let variant_arms = variants
@@ -1409,7 +1420,7 @@ fn deserialize_adjacently_tagged_enum(
split_with_de_lifetime(params);
let delife = params.borrowed.de_lifetime();
let (variants_stmt, variant_visitor) = prepare_enum_variant_enum(variants, cattrs);
let (variants_stmt, variant_visitor) = prepare_enum_variant_enum(variants);
let variant_arms: &Vec<_> = &variants
.iter()
@@ -1985,7 +1996,7 @@ fn deserialize_untagged_newtype_variant(
fn deserialize_generated_identifier(
fields: &[(&str, Ident, &BTreeSet<String>)],
cattrs: &attr::Container,
has_flatten: bool,
is_variant: bool,
ignore_variant: Option<TokenStream>,
fallthrough: Option<TokenStream>,
@@ -1999,11 +2010,11 @@ fn deserialize_generated_identifier(
is_variant,
fallthrough,
None,
!is_variant && cattrs.has_flatten(),
!is_variant && has_flatten,
None,
));
let lifetime = if !is_variant && cattrs.has_flatten() {
let lifetime = if !is_variant && has_flatten {
Some(quote!(<'de>))
} else {
None
@@ -2043,8 +2054,9 @@ fn deserialize_generated_identifier(
fn deserialize_field_identifier(
fields: &[(&str, Ident, &BTreeSet<String>)],
cattrs: &attr::Container,
has_flatten: bool,
) -> Stmts {
let (ignore_variant, fallthrough) = if cattrs.has_flatten() {
let (ignore_variant, fallthrough) = if has_flatten {
let ignore_variant = quote!(__other(_serde::__private::de::Content<'de>),);
let fallthrough = quote!(_serde::__private::Ok(__Field::__other(__value)));
(Some(ignore_variant), Some(fallthrough))
@@ -2058,7 +2070,7 @@ fn deserialize_field_identifier(
Stmts(deserialize_generated_identifier(
fields,
cattrs,
has_flatten,
false,
ignore_variant,
fallthrough,
@@ -2460,6 +2472,7 @@ fn deserialize_map(
params: &Parameters,
fields: &[Field],
cattrs: &attr::Container,
has_flatten: bool,
) -> Fragment {
// Create the field names for the fields.
let fields_names: Vec<_> = fields
@@ -2480,9 +2493,6 @@ fn deserialize_map(
});
// Collect contents for flatten fields into a buffer
let has_flatten = fields
.iter()
.any(|field| field.attrs.flatten() && !field.attrs.skip_deserializing());
let let_collect = if has_flatten {
Some(quote! {
let mut __collect = _serde::__private::Vec::<_serde::__private::Option<(
@@ -2681,7 +2691,10 @@ fn deserialize_map_in_place(
fields: &[Field],
cattrs: &attr::Container,
) -> Fragment {
assert!(!cattrs.has_flatten());
assert!(
!has_flatten(fields),
"inplace deserialization of maps does not support flatten fields"
);
// Create the field names for the fields.
let fields_names: Vec<_> = fields
@@ -3014,6 +3027,14 @@ fn effective_style(variant: &Variant) -> Style {
}
}
/// True if there is any field with a `#[serde(flatten)]` attribute, other than
/// fields which are skipped.
fn has_flatten(fields: &[Field]) -> bool {
fields
.iter()
.any(|field| field.attrs.flatten() && !field.attrs.skip_deserializing())
}
struct DeImplGenerics<'a>(&'a Parameters);
#[cfg(feature = "deserialize_in_place")]
struct InPlaceImplGenerics<'a>(&'a Parameters);
+1 -12
View File
@@ -63,7 +63,7 @@ impl<'a> Container<'a> {
item: &'a syn::DeriveInput,
derive: Derive,
) -> Option<Container<'a>> {
let mut attrs = attr::Container::from_ast(cx, item);
let attrs = attr::Container::from_ast(cx, item);
let mut data = match &item.data {
syn::Data::Enum(data) => Data::Enum(enum_from_ast(cx, &data.variants, attrs.default())),
@@ -77,15 +77,11 @@ impl<'a> Container<'a> {
}
};
let mut has_flatten = false;
match &mut data {
Data::Enum(variants) => {
for variant in variants {
variant.attrs.rename_by_rules(attrs.rename_all_rules());
for field in &mut variant.fields {
if field.attrs.flatten() {
has_flatten = true;
}
field.attrs.rename_by_rules(
variant
.attrs
@@ -97,18 +93,11 @@ impl<'a> Container<'a> {
}
Data::Struct(_, fields) => {
for field in fields {
if field.attrs.flatten() {
has_flatten = true;
}
field.attrs.rename_by_rules(attrs.rename_all_rules());
}
}
}
if has_flatten {
attrs.mark_has_flatten();
}
let mut item = Container {
ident: item.ident.clone(),
attrs,
-10
View File
@@ -216,7 +216,6 @@ pub struct Container {
type_into: Option<syn::Type>,
remote: Option<syn::Path>,
identifier: Identifier,
has_flatten: bool,
serde_path: Option<syn::Path>,
is_packed: bool,
/// Error message generated when type can't be deserialized
@@ -587,7 +586,6 @@ impl Container {
type_into: type_into.get(),
remote: remote.get(),
identifier: decide_identifier(cx, item, field_identifier, variant_identifier),
has_flatten: false,
serde_path: serde_path.get(),
is_packed,
expecting: expecting.get(),
@@ -655,14 +653,6 @@ impl Container {
self.identifier
}
pub fn has_flatten(&self) -> bool {
self.has_flatten
}
pub fn mark_has_flatten(&mut self) {
self.has_flatten = true;
}
pub fn custom_serde_path(&self) -> Option<&syn::Path> {
self.serde_path.as_ref()
}
+1 -1
View File
@@ -13,7 +13,7 @@
//!
//! [https://serde.rs/derive.html]: https://serde.rs/derive.html
#![doc(html_root_url = "https://docs.rs/serde_derive/1.0.205")]
#![doc(html_root_url = "https://docs.rs/serde_derive/1.0.210")]
#![cfg_attr(not(check_cfg), allow(unexpected_cfgs))]
// Ignored clippy lints
#![allow(
+12 -21
View File
@@ -289,9 +289,18 @@ fn serialize_tuple_struct(
}
fn serialize_struct(params: &Parameters, fields: &[Field], cattrs: &attr::Container) -> Fragment {
assert!(fields.len() as u64 <= u64::from(u32::MAX));
assert!(
fields.len() as u64 <= u64::from(u32::MAX),
"too many fields in {}: {}, maximum supported count is {}",
cattrs.name().serialize_name(),
fields.len(),
u32::MAX,
);
if cattrs.has_flatten() {
let has_non_skipped_flatten = fields
.iter()
.any(|field| field.attrs.flatten() && !field.attrs.skip_serializing());
if has_non_skipped_flatten {
serialize_struct_as_map(params, fields, cattrs)
} else {
serialize_struct_as_struct(params, fields, cattrs)
@@ -370,26 +379,8 @@ fn serialize_struct_as_map(
let let_mut = mut_if(serialized_fields.peek().is_some() || tag_field_exists);
let len = if cattrs.has_flatten() {
quote!(_serde::__private::None)
} else {
let len = serialized_fields
.map(|field| match field.attrs.skip_serializing_if() {
None => quote!(1),
Some(path) => {
let field_expr = get_member(params, field, &field.member);
quote!(if #path(#field_expr) { 0 } else { 1 })
}
})
.fold(
quote!(#tag_field_exists as usize),
|sum, expr| quote!(#sum + #expr),
);
quote!(_serde::__private::Some(#len))
};
quote_block! {
let #let_mut __serde_state = _serde::Serializer::serialize_map(__serializer, #len)?;
let #let_mut __serde_state = _serde::Serializer::serialize_map(__serializer, _serde::__private::None)?;
#tag_field
#(#serialize_fields)*
_serde::ser::SerializeMap::end(__serde_state)
+66
View File
@@ -0,0 +1,66 @@
#![allow(dead_code)] // we do not read enum fields
use serde_derive::Deserialize;
#[derive(Deserialize)]
pub struct Nested;
#[derive(Deserialize)]
pub enum ExternallyTagged1 {
Tuple(f64, String),
Flatten {
#[serde(flatten)]
nested: Nested,
},
}
#[derive(Deserialize)]
pub enum ExternallyTagged2 {
Flatten {
#[serde(flatten)]
nested: Nested,
},
Tuple(f64, String),
}
// Internally tagged enums cannot contain tuple variants so not tested here
#[derive(Deserialize)]
#[serde(tag = "tag", content = "content")]
pub enum AdjacentlyTagged1 {
Tuple(f64, String),
Flatten {
#[serde(flatten)]
nested: Nested,
},
}
#[derive(Deserialize)]
#[serde(tag = "tag", content = "content")]
pub enum AdjacentlyTagged2 {
Flatten {
#[serde(flatten)]
nested: Nested,
},
Tuple(f64, String),
}
#[derive(Deserialize)]
#[serde(untagged)]
pub enum Untagged1 {
Tuple(f64, String),
Flatten {
#[serde(flatten)]
nested: Nested,
},
}
#[derive(Deserialize)]
#[serde(untagged)]
pub enum Untagged2 {
Flatten {
#[serde(flatten)]
nested: Nested,
},
Tuple(f64, String),
}
+41
View File
@@ -0,0 +1,41 @@
use serde_derive::{Serialize, Deserialize};
use serde_test::{assert_tokens, Token};
#[derive(Serialize, Deserialize, Debug, PartialEq)]
enum Enum {
Simple {
a: i32,
},
Flatten {
#[serde(flatten)]
flatten: (),
a: i32,
},
}
#[test]
fn simple_variant() {
assert_tokens(
&Enum::Simple { a: 42 },
&[
Token::StructVariant { name: "Enum", variant: "Simple", len: 1 },
Token::Str("a"),
Token::I32(42),
Token::StructVariantEnd,
]
);
}
#[test]
fn flatten_variant() {
assert_tokens(
&Enum::Flatten { flatten: (), a: 42 },
&[
Token::NewtypeVariant { name: "Enum", variant: "Flatten" },
Token::Map { len: None },
Token::Str("a"),
Token::I32(42),
Token::MapEnd,
]
);
}
+18
View File
@@ -0,0 +1,18 @@
#![allow(dead_code)] // we do not read enum fields
use serde_derive::Deserialize;
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
pub enum A {
B {
c: String,
},
D {
#[serde(flatten)]
e: E,
},
}
#[derive(Deserialize)]
pub struct E {}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,799 @@
#![deny(trivial_numeric_casts)]
#![allow(
clippy::derive_partial_eq_without_eq,
clippy::enum_variant_names,
clippy::redundant_field_names,
clippy::too_many_lines
)]
use serde_derive::{Deserialize, Serialize};
use serde_test::{assert_de_tokens, assert_de_tokens_error, assert_tokens, Token};
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(tag = "t", content = "c")]
enum AdjacentlyTagged<T> {
Unit,
Newtype(T),
Tuple(u8, u8),
Struct { f: u8 },
}
mod unit {
use super::*;
#[test]
fn map_str_tag_only() {
// Map: tag only
assert_tokens(
&AdjacentlyTagged::Unit::<u8>,
&[
Token::Struct {
name: "AdjacentlyTagged",
len: 1,
},
Token::Str("t"),
Token::UnitVariant {
name: "AdjacentlyTagged",
variant: "Unit",
},
Token::StructEnd,
],
);
// Map: tag only and incorrect hint for number of elements
assert_de_tokens(
&AdjacentlyTagged::Unit::<u8>,
&[
Token::Struct {
name: "AdjacentlyTagged",
len: 2,
},
Token::Str("t"),
Token::UnitVariant {
name: "AdjacentlyTagged",
variant: "Unit",
},
Token::StructEnd,
],
);
}
#[test]
fn map_int_tag_only() {
// Map: tag (as number) only
assert_de_tokens(
&AdjacentlyTagged::Unit::<u8>,
&[
Token::Struct {
name: "AdjacentlyTagged",
len: 1,
},
Token::U16(0),
Token::UnitVariant {
name: "AdjacentlyTagged",
variant: "Unit",
},
Token::StructEnd,
],
);
}
#[test]
fn map_bytes_tag_only() {
// Map: tag only
assert_de_tokens(
&AdjacentlyTagged::Unit::<u8>,
&[
Token::Struct {
name: "AdjacentlyTagged",
len: 1,
},
Token::Bytes(b"t"),
Token::UnitVariant {
name: "AdjacentlyTagged",
variant: "Unit",
},
Token::StructEnd,
],
);
// Map: tag only
assert_de_tokens(
&AdjacentlyTagged::Unit::<u8>,
&[
Token::Struct {
name: "AdjacentlyTagged",
len: 1,
},
Token::BorrowedBytes(b"t"),
Token::UnitVariant {
name: "AdjacentlyTagged",
variant: "Unit",
},
Token::StructEnd,
],
);
}
#[test]
fn map_str_tag_content() {
// Map: tag + content
assert_de_tokens(
&AdjacentlyTagged::Unit::<u8>,
&[
Token::Struct {
name: "AdjacentlyTagged",
len: 2,
},
Token::Str("t"),
Token::UnitVariant {
name: "AdjacentlyTagged",
variant: "Unit",
},
Token::Str("c"),
Token::Unit,
Token::StructEnd,
],
);
// Map: content + tag
assert_de_tokens(
&AdjacentlyTagged::Unit::<u8>,
&[
Token::Struct {
name: "AdjacentlyTagged",
len: 2,
},
Token::Str("c"),
Token::Unit,
Token::Str("t"),
Token::UnitVariant {
name: "AdjacentlyTagged",
variant: "Unit",
},
Token::StructEnd,
],
);
// Map: tag + content + excess fields (f, g, h)
assert_de_tokens(
&AdjacentlyTagged::Unit::<u8>,
&[
Token::Struct {
name: "AdjacentlyTagged",
len: 2,
},
Token::Str("f"),
Token::Unit,
Token::Str("t"),
Token::UnitVariant {
name: "AdjacentlyTagged",
variant: "Unit",
},
Token::Str("g"),
Token::Unit,
Token::Str("c"),
Token::Unit,
Token::Str("h"),
Token::Unit,
Token::StructEnd,
],
);
}
#[test]
fn map_int_tag_content() {
// Map: tag (as number) + content (as number)
assert_de_tokens(
&AdjacentlyTagged::Unit::<u8>,
&[
Token::Struct {
name: "AdjacentlyTagged",
len: 2,
},
Token::U8(0),
Token::UnitVariant {
name: "AdjacentlyTagged",
variant: "Unit",
},
Token::U8(1),
Token::Unit,
Token::StructEnd,
],
);
// Map: content (as number) + tag (as number)
assert_de_tokens(
&AdjacentlyTagged::Unit::<u8>,
&[
Token::Struct {
name: "AdjacentlyTagged",
len: 2,
},
Token::U64(1),
Token::Unit,
Token::U64(0),
Token::UnitVariant {
name: "AdjacentlyTagged",
variant: "Unit",
},
Token::StructEnd,
],
);
}
#[test]
fn map_bytes_tag_content() {
// Map: tag + content
assert_de_tokens(
&AdjacentlyTagged::Unit::<u8>,
&[
Token::Struct {
name: "AdjacentlyTagged",
len: 2,
},
Token::BorrowedBytes(b"t"),
Token::UnitVariant {
name: "AdjacentlyTagged",
variant: "Unit",
},
Token::BorrowedBytes(b"c"),
Token::Unit,
Token::StructEnd,
],
);
// Map: content + tag
assert_de_tokens(
&AdjacentlyTagged::Unit::<u8>,
&[
Token::Struct {
name: "AdjacentlyTagged",
len: 2,
},
Token::Bytes(b"c"),
Token::Unit,
Token::Bytes(b"t"),
Token::UnitVariant {
name: "AdjacentlyTagged",
variant: "Unit",
},
Token::StructEnd,
],
);
}
#[test]
fn seq_tag_content() {
// Seq: tag and content
assert_de_tokens(
&AdjacentlyTagged::Unit::<u8>,
&[
Token::Seq { len: Some(2) },
Token::UnitVariant {
name: "AdjacentlyTagged",
variant: "Unit",
},
Token::Unit,
Token::SeqEnd,
],
);
// Seq: tag (as string) and content
assert_de_tokens(
&AdjacentlyTagged::Unit::<u8>,
&[
Token::Seq { len: None },
Token::Str("Unit"), // tag
Token::Unit, // content
Token::SeqEnd,
],
);
// Seq: tag (as borrowed string) and content
assert_de_tokens(
&AdjacentlyTagged::Unit::<u8>,
&[
Token::Seq { len: None },
Token::BorrowedStr("Unit"), // tag
Token::Unit, // content
Token::SeqEnd,
],
);
}
}
mod newtype {
use super::*;
#[test]
fn map_tag_only() {
// optional newtype with no content field
assert_de_tokens(
&AdjacentlyTagged::Newtype::<Option<u8>>(None),
&[
Token::Struct {
name: "AdjacentlyTagged",
len: 1,
},
Token::Str("t"),
Token::UnitVariant {
name: "AdjacentlyTagged",
variant: "Newtype",
},
Token::StructEnd,
],
);
}
#[test]
fn map_tag_content() {
let value = AdjacentlyTagged::Newtype::<u8>(1);
// Map: tag + content
assert_tokens(
&value,
&[
Token::Struct {
name: "AdjacentlyTagged",
len: 2,
},
Token::Str("t"),
Token::UnitVariant {
name: "AdjacentlyTagged",
variant: "Newtype",
},
Token::Str("c"),
Token::U8(1),
Token::StructEnd,
],
);
// Map: content + tag
assert_de_tokens(
&value,
&[
Token::Struct {
name: "AdjacentlyTagged",
len: 2,
},
Token::Str("c"),
Token::U8(1),
Token::Str("t"),
Token::UnitVariant {
name: "AdjacentlyTagged",
variant: "Newtype",
},
Token::StructEnd,
],
);
}
#[test]
fn seq() {
let value = AdjacentlyTagged::Newtype::<u8>(1);
// Seq: tag and content
assert_de_tokens(
&value,
&[
Token::Seq { len: Some(2) },
Token::UnitVariant {
name: "AdjacentlyTagged",
variant: "Newtype",
},
Token::U8(1),
Token::SeqEnd,
],
);
// Seq: tag (as string) and content
assert_de_tokens(
&value,
&[
Token::Seq { len: None },
Token::Str("Newtype"), // tag
Token::U8(1), // content
Token::SeqEnd,
],
);
// Seq: tag (as borrowed string) and content
assert_de_tokens(
&value,
&[
Token::Seq { len: None },
Token::BorrowedStr("Newtype"), // tag
Token::U8(1), // content
Token::SeqEnd,
],
);
}
}
#[test]
fn newtype_with_newtype() {
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct NewtypeStruct(u32);
assert_de_tokens(
&AdjacentlyTagged::Newtype(NewtypeStruct(5)),
&[
Token::Struct {
name: "AdjacentlyTagged",
len: 2,
},
Token::Str("c"),
Token::NewtypeStruct {
name: "NewtypeStruct",
},
Token::U32(5),
Token::Str("t"),
Token::UnitVariant {
name: "AdjacentlyTagged",
variant: "Newtype",
},
Token::StructEnd,
],
);
}
mod tuple {
use super::*;
#[test]
fn map() {
let value = AdjacentlyTagged::Tuple::<u8>(1, 1);
// Map: tag + content
assert_tokens(
&value,
&[
Token::Struct {
name: "AdjacentlyTagged",
len: 2,
},
Token::Str("t"),
Token::UnitVariant {
name: "AdjacentlyTagged",
variant: "Tuple",
},
Token::Str("c"),
Token::Tuple { len: 2 },
Token::U8(1),
Token::U8(1),
Token::TupleEnd,
Token::StructEnd,
],
);
// Map: content + tag
assert_de_tokens(
&value,
&[
Token::Struct {
name: "AdjacentlyTagged",
len: 2,
},
Token::Str("c"),
Token::Tuple { len: 2 },
Token::U8(1),
Token::U8(1),
Token::TupleEnd,
Token::Str("t"),
Token::UnitVariant {
name: "AdjacentlyTagged",
variant: "Tuple",
},
Token::StructEnd,
],
);
}
#[test]
fn seq() {
let value = AdjacentlyTagged::Tuple::<u8>(1, 1);
// Seq: tag + content
assert_de_tokens(
&value,
&[
Token::Seq { len: Some(2) },
Token::UnitVariant {
name: "AdjacentlyTagged",
variant: "Tuple",
},
Token::Tuple { len: 2 },
Token::U8(1),
Token::U8(1),
Token::TupleEnd,
Token::SeqEnd,
],
);
}
}
mod struct_ {
use super::*;
#[test]
fn map() {
let value = AdjacentlyTagged::Struct::<u8> { f: 1 };
// Map: tag + content
assert_tokens(
&value,
&[
Token::Struct {
name: "AdjacentlyTagged",
len: 2,
},
Token::Str("t"),
Token::UnitVariant {
name: "AdjacentlyTagged",
variant: "Struct",
},
Token::Str("c"),
Token::Struct {
name: "Struct",
len: 1,
},
Token::Str("f"),
Token::U8(1),
Token::StructEnd,
Token::StructEnd,
],
);
// Map: content + tag
assert_de_tokens(
&value,
&[
Token::Struct {
name: "AdjacentlyTagged",
len: 2,
},
Token::Str("c"),
Token::Struct {
name: "Struct",
len: 1,
},
Token::Str("f"),
Token::U8(1),
Token::StructEnd,
Token::Str("t"),
Token::UnitVariant {
name: "AdjacentlyTagged",
variant: "Struct",
},
Token::StructEnd,
],
);
}
#[test]
fn seq() {
let value = AdjacentlyTagged::Struct::<u8> { f: 1 };
// Seq: tag + content
assert_de_tokens(
&value,
&[
Token::Seq { len: Some(2) },
Token::UnitVariant {
name: "AdjacentlyTagged",
variant: "Struct",
},
Token::Struct {
name: "Struct",
len: 1,
},
Token::Str("f"),
Token::U8(1),
Token::StructEnd,
Token::SeqEnd,
],
);
}
}
#[test]
fn struct_with_flatten() {
#[derive(Serialize, Deserialize, PartialEq, Debug)]
#[serde(tag = "t", content = "c")]
enum Data {
A {
a: i32,
#[serde(flatten)]
flat: Flat,
},
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Flat {
b: i32,
}
let data = Data::A {
a: 0,
flat: Flat { b: 0 },
};
assert_tokens(
&data,
&[
Token::Struct {
name: "Data",
len: 2,
},
Token::Str("t"),
Token::UnitVariant {
name: "Data",
variant: "A",
},
Token::Str("c"),
Token::Map { len: None },
Token::Str("a"),
Token::I32(0),
Token::Str("b"),
Token::I32(0),
Token::MapEnd,
Token::StructEnd,
],
);
}
#[test]
fn expecting_message() {
#[derive(Deserialize)]
#[serde(tag = "tag", content = "content")]
#[serde(expecting = "something strange...")]
enum Enum {
AdjacentlyTagged,
}
assert_de_tokens_error::<Enum>(
&[Token::Str("AdjacentlyTagged")],
r#"invalid type: string "AdjacentlyTagged", expected something strange..."#,
);
assert_de_tokens_error::<Enum>(
&[Token::Map { len: None }, Token::Unit],
r#"invalid type: unit value, expected "tag", "content", or other ignored fields"#,
);
// Check that #[serde(expecting = "...")] doesn't affect variant identifier error message
assert_de_tokens_error::<Enum>(
&[Token::Map { len: None }, Token::Str("tag"), Token::Unit],
"invalid type: unit value, expected variant of enum Enum",
);
}
#[test]
fn partially_untagged() {
#[derive(Serialize, Deserialize, PartialEq, Debug)]
#[serde(tag = "t", content = "c")]
enum Data {
A(u32),
B,
#[serde(untagged)]
Var(u32),
}
let data = Data::A(7);
assert_de_tokens(
&data,
&[
Token::Map { len: None },
Token::Str("t"),
Token::Str("A"),
Token::Str("c"),
Token::U32(7),
Token::MapEnd,
],
);
let data = Data::Var(42);
assert_de_tokens(&data, &[Token::U32(42)]);
// TODO test error output
}
#[test]
fn deny_unknown_fields() {
#[derive(Debug, PartialEq, Deserialize)]
#[serde(tag = "t", content = "c", deny_unknown_fields)]
enum AdjacentlyTagged {
Unit,
}
assert_de_tokens(
&AdjacentlyTagged::Unit,
&[
Token::Struct {
name: "AdjacentlyTagged",
len: 2,
},
Token::Str("t"),
Token::UnitVariant {
name: "AdjacentlyTagged",
variant: "Unit",
},
Token::Str("c"),
Token::Unit,
Token::StructEnd,
],
);
assert_de_tokens_error::<AdjacentlyTagged>(
&[
Token::Struct {
name: "AdjacentlyTagged",
len: 2,
},
Token::Str("t"),
Token::UnitVariant {
name: "AdjacentlyTagged",
variant: "Unit",
},
Token::Str("c"),
Token::Unit,
Token::Str("h"),
],
r#"invalid value: string "h", expected "t" or "c""#,
);
assert_de_tokens_error::<AdjacentlyTagged>(
&[
Token::Struct {
name: "AdjacentlyTagged",
len: 2,
},
Token::Str("h"),
],
r#"invalid value: string "h", expected "t" or "c""#,
);
assert_de_tokens_error::<AdjacentlyTagged>(
&[
Token::Struct {
name: "AdjacentlyTagged",
len: 2,
},
Token::Str("c"),
Token::Unit,
Token::Str("h"),
],
r#"invalid value: string "h", expected "t" or "c""#,
);
assert_de_tokens_error::<AdjacentlyTagged>(
&[
Token::Struct {
name: "AdjacentlyTagged",
len: 2,
},
Token::U64(0), // tag field
Token::UnitVariant {
name: "AdjacentlyTagged",
variant: "Unit",
},
Token::U64(3),
],
r#"invalid value: integer `3`, expected "t" or "c""#,
);
assert_de_tokens_error::<AdjacentlyTagged>(
&[
Token::Struct {
name: "AdjacentlyTagged",
len: 2,
},
Token::Bytes(b"c"),
Token::Unit,
Token::Bytes(b"h"),
],
r#"invalid value: byte array, expected "t" or "c""#,
);
}
File diff suppressed because it is too large Load Diff
+583
View File
@@ -0,0 +1,583 @@
#![deny(trivial_numeric_casts)]
#![allow(
clippy::derive_partial_eq_without_eq,
clippy::enum_variant_names,
clippy::redundant_field_names,
clippy::too_many_lines
)]
mod bytes;
use serde_derive::{Deserialize, Serialize};
use serde_test::{assert_de_tokens, assert_de_tokens_error, assert_tokens, Token};
use std::collections::BTreeMap;
#[test]
fn complex() {
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
enum Untagged {
A { a: u8 },
B { b: u8 },
C,
D(u8),
E(String),
F(u8, u8),
}
assert_tokens(
&Untagged::A { a: 1 },
&[
Token::Struct {
name: "Untagged",
len: 1,
},
Token::Str("a"),
Token::U8(1),
Token::StructEnd,
],
);
assert_tokens(
&Untagged::B { b: 2 },
&[
Token::Struct {
name: "Untagged",
len: 1,
},
Token::Str("b"),
Token::U8(2),
Token::StructEnd,
],
);
// Serializes to unit, deserializes from either depending on format's
// preference.
assert_tokens(&Untagged::C, &[Token::Unit]);
assert_de_tokens(&Untagged::C, &[Token::None]);
assert_tokens(&Untagged::D(4), &[Token::U8(4)]);
assert_tokens(&Untagged::E("e".to_owned()), &[Token::Str("e")]);
assert_tokens(
&Untagged::F(1, 2),
&[
Token::Tuple { len: 2 },
Token::U8(1),
Token::U8(2),
Token::TupleEnd,
],
);
assert_de_tokens_error::<Untagged>(
&[Token::Tuple { len: 1 }, Token::U8(1), Token::TupleEnd],
"data did not match any variant of untagged enum Untagged",
);
assert_de_tokens_error::<Untagged>(
&[
Token::Tuple { len: 3 },
Token::U8(1),
Token::U8(2),
Token::U8(3),
Token::TupleEnd,
],
"data did not match any variant of untagged enum Untagged",
);
}
#[test]
fn newtype_unit_and_empty_map() {
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Unit;
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
enum Message {
Unit(Unit),
Map(BTreeMap<String, String>),
}
assert_tokens(
&Message::Map(BTreeMap::new()),
&[Token::Map { len: Some(0) }, Token::MapEnd],
);
}
// Reaches crate::private::de::content::ContentRefDeserializer::deserialize_newtype_struct
#[test]
fn newtype_struct() {
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct NewtypeStruct(u32);
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
enum E {
Newtype(NewtypeStruct),
Null,
}
let value = E::Newtype(NewtypeStruct(5));
// Content::Newtype case
assert_tokens(
&value,
&[
Token::NewtypeStruct {
name: "NewtypeStruct",
},
Token::U32(5),
],
);
// _ case
assert_de_tokens(&value, &[Token::U32(5)]);
}
mod newtype_enum {
use super::*;
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
enum Outer {
Inner(Inner),
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
enum Inner {
Unit,
Newtype(u8),
Tuple0(),
Tuple2(u8, u8),
Struct { f: u8 },
EmptyStruct {},
}
// Reaches crate::private::de::content::VariantRefDeserializer::unit_variant
#[test]
fn unit() {
assert_tokens(
&Outer::Inner(Inner::Unit),
&[Token::UnitVariant {
name: "Inner",
variant: "Unit",
}],
);
}
// Reaches crate::private::de::content::VariantRefDeserializer::newtype_variant_seed
#[test]
fn newtype() {
assert_tokens(
&Outer::Inner(Inner::Newtype(1)),
&[
Token::NewtypeVariant {
name: "Inner",
variant: "Newtype",
},
Token::U8(1),
],
);
}
// Reaches crate::private::de::content::VariantRefDeserializer::tuple_variant
#[test]
fn tuple0() {
assert_tokens(
&Outer::Inner(Inner::Tuple0()),
&[
Token::TupleVariant {
name: "Inner",
variant: "Tuple0",
len: 0,
},
Token::TupleVariantEnd,
],
);
}
// Reaches crate::private::de::content::VariantRefDeserializer::tuple_variant
#[test]
fn tuple2() {
assert_tokens(
&Outer::Inner(Inner::Tuple2(1, 1)),
&[
Token::TupleVariant {
name: "Inner",
variant: "Tuple2",
len: 2,
},
Token::U8(1),
Token::U8(1),
Token::TupleVariantEnd,
],
);
}
// Reaches crate::private::de::content::VariantRefDeserializer::struct_variant
// Content::Map case
#[test]
fn struct_from_map() {
assert_tokens(
&Outer::Inner(Inner::Struct { f: 1 }),
&[
Token::StructVariant {
name: "Inner",
variant: "Struct",
len: 1,
},
Token::Str("f"),
Token::U8(1),
Token::StructVariantEnd,
],
);
}
// Reaches crate::private::de::content::VariantRefDeserializer::struct_variant
// Content::Seq case
#[test]
fn struct_from_seq() {
assert_de_tokens(
&Outer::Inner(Inner::Struct { f: 1 }),
&[
Token::Map { len: Some(1) },
// tag
Token::Str("Struct"),
// content
Token::Seq { len: Some(1) },
Token::U8(1),
Token::SeqEnd,
Token::MapEnd,
],
);
}
// Reaches crate::private::de::content::VariantRefDeserializer::struct_variant
// Content::Map case
// Special case - empty map
#[test]
fn empty_struct_from_map() {
assert_de_tokens(
&Outer::Inner(Inner::EmptyStruct {}),
&[
Token::Map { len: Some(1) },
// tag
Token::Str("EmptyStruct"),
// content
Token::Map { len: Some(0) },
Token::MapEnd,
Token::MapEnd,
],
);
}
// Reaches crate::private::de::content::VariantRefDeserializer::struct_variant
// Content::Seq case
// Special case - empty seq
#[test]
fn empty_struct_from_seq() {
assert_de_tokens(
&Outer::Inner(Inner::EmptyStruct {}),
&[
Token::Map { len: Some(1) },
// tag
Token::Str("EmptyStruct"),
// content
Token::Seq { len: Some(0) },
Token::SeqEnd,
Token::MapEnd,
],
);
}
}
// Reaches crate::private::de::content::ContentRefDeserializer::deserialize_option
mod with_optional_field {
use super::*;
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
enum Enum {
Struct { optional: Option<u32> },
Null,
}
#[test]
fn some() {
assert_tokens(
&Enum::Struct { optional: Some(42) },
&[
Token::Struct {
name: "Enum",
len: 1,
},
Token::Str("optional"),
Token::Some,
Token::U32(42),
Token::StructEnd,
],
);
}
#[test]
fn some_without_marker() {
assert_de_tokens(
&Enum::Struct { optional: Some(42) },
&[
Token::Struct {
name: "Enum",
len: 1,
},
Token::Str("optional"),
Token::U32(42),
Token::StructEnd,
],
);
}
#[test]
fn none() {
assert_tokens(
&Enum::Struct { optional: None },
&[
Token::Struct {
name: "Enum",
len: 1,
},
Token::Str("optional"),
Token::None,
Token::StructEnd,
],
);
}
#[test]
fn unit() {
assert_de_tokens(
&Enum::Struct { optional: None },
&[
Token::Map { len: None },
Token::Str("optional"),
Token::Unit,
Token::MapEnd,
],
);
}
}
#[test]
fn string_and_bytes() {
#[derive(Debug, PartialEq, Deserialize)]
#[serde(untagged)]
enum Untagged {
String {
string: String,
},
Bytes {
#[serde(with = "bytes")]
bytes: Vec<u8>,
},
}
assert_de_tokens(
&Untagged::String {
string: "\0".to_owned(),
},
&[
Token::Struct {
name: "Untagged",
len: 1,
},
Token::Str("string"),
Token::Str("\0"),
Token::StructEnd,
],
);
assert_de_tokens(
&Untagged::String {
string: "\0".to_owned(),
},
&[
Token::Struct {
name: "Untagged",
len: 1,
},
Token::Str("string"),
Token::String("\0"),
Token::StructEnd,
],
);
assert_de_tokens(
&Untagged::String {
string: "\0".to_owned(),
},
&[
Token::Struct {
name: "Untagged",
len: 1,
},
Token::Str("string"),
Token::Bytes(b"\0"),
Token::StructEnd,
],
);
assert_de_tokens(
&Untagged::String {
string: "\0".to_owned(),
},
&[
Token::Struct {
name: "Untagged",
len: 1,
},
Token::Str("string"),
Token::ByteBuf(b"\0"),
Token::StructEnd,
],
);
assert_de_tokens(
&Untagged::Bytes { bytes: vec![0] },
&[
Token::Struct {
name: "Untagged",
len: 1,
},
Token::Str("bytes"),
Token::Str("\0"),
Token::StructEnd,
],
);
assert_de_tokens(
&Untagged::Bytes { bytes: vec![0] },
&[
Token::Struct {
name: "Untagged",
len: 1,
},
Token::Str("bytes"),
Token::String("\0"),
Token::StructEnd,
],
);
assert_de_tokens(
&Untagged::Bytes { bytes: vec![0] },
&[
Token::Struct {
name: "Untagged",
len: 1,
},
Token::Str("bytes"),
Token::Bytes(b"\0"),
Token::StructEnd,
],
);
assert_de_tokens(
&Untagged::Bytes { bytes: vec![0] },
&[
Token::Struct {
name: "Untagged",
len: 1,
},
Token::Str("bytes"),
Token::ByteBuf(b"\0"),
Token::StructEnd,
],
);
assert_de_tokens(
&Untagged::Bytes { bytes: vec![0] },
&[
Token::Struct {
name: "Untagged",
len: 1,
},
Token::Str("bytes"),
Token::Seq { len: Some(1) },
Token::U8(0),
Token::SeqEnd,
Token::StructEnd,
],
);
}
#[test]
fn contains_flatten() {
#[derive(Serialize, Deserialize, PartialEq, Debug)]
#[serde(untagged)]
enum Data {
A {
a: i32,
#[serde(flatten)]
flat: Flat,
},
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Flat {
b: i32,
}
let data = Data::A {
a: 0,
flat: Flat { b: 0 },
};
assert_tokens(
&data,
&[
Token::Map { len: None },
Token::Str("a"),
Token::I32(0),
Token::Str("b"),
Token::I32(0),
Token::MapEnd,
],
);
}
#[test]
fn contains_flatten_with_integer_key() {
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Untagged {
Variant {
#[serde(flatten)]
map: BTreeMap<u64, String>,
},
}
assert_tokens(
&Untagged::Variant {
map: {
let mut map = BTreeMap::new();
map.insert(100, "BTreeMap".to_owned());
map
},
},
&[
Token::Map { len: None },
Token::U64(100),
Token::Str("BTreeMap"),
Token::MapEnd,
],
);
}
#[test]
fn expecting_message() {
#[derive(Deserialize)]
#[serde(untagged)]
#[serde(expecting = "something strange...")]
enum Enum {
Untagged,
}
assert_de_tokens_error::<Enum>(&[Token::Str("Untagged")], "something strange...");
}
+19
View File
@@ -547,6 +547,12 @@ fn test_gen() {
}
assert::<FlattenWith>();
#[derive(Serialize, Deserialize)]
pub struct Flatten<T> {
#[serde(flatten)]
t: T,
}
#[derive(Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct FlattenDenyUnknown<T> {
@@ -554,6 +560,19 @@ fn test_gen() {
t: T,
}
#[derive(Serialize, Deserialize)]
pub struct SkipDeserializing<T> {
#[serde(skip_deserializing)]
flat: T,
}
#[derive(Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SkipDeserializingDenyUnknown<T> {
#[serde(skip_deserializing)]
flat: T,
}
#[derive(Serialize, Deserialize)]
pub struct StaticStrStruct<'a> {
a: &'a str,
File diff suppressed because it is too large Load Diff