aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Walbran <qwandor@google.com>2020-12-10 13:15:48 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2020-12-10 13:15:48 +0000
commit2d734ab9cde89f35e5d5a445bd385fe93bb18e9c (patch)
tree7c114286a7460144219e17f26d3c1bc42c265185
parente99a47351cda5294c4a9ce7f2935fda374e97f65 (diff)
parentf808f81f4da77fee6407473a12f904694d95e9a0 (diff)
downloadserde_derive-2d734ab9cde89f35e5d5a445bd385fe93bb18e9c.tar.gz
Import serde_derive crate. am: 8a1b5c6df7 am: f808f81f4d
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/serde_derive/+/1521557 MUST ONLY BE SUBMITTED BY AUTOMERGER Change-Id: Iefd5f26c023b6b237faa43336a9962d9079dc9c6
-rw-r--r--.cargo_vcs_info.json5
-rw-r--r--Android.bp23
-rw-r--r--Cargo.toml45
-rw-r--r--Cargo.toml.orig31
l---------LICENSE1
-rw-r--r--LICENSE-APACHE201
-rw-r--r--LICENSE-MIT23
-rw-r--r--METADATA19
-rw-r--r--MODULE_LICENSE_APACHE20
-rw-r--r--OWNERS1
-rw-r--r--README.md111
-rw-r--r--build.rs30
-rw-r--r--crates-io.md62
-rw-r--r--src/bound.rs317
-rw-r--r--src/de.rs3049
-rw-r--r--src/dummy.rs48
-rw-r--r--src/fragment.rs74
-rw-r--r--src/internals/ast.rs202
-rw-r--r--src/internals/attr.rs1961
-rw-r--r--src/internals/case.rs174
-rw-r--r--src/internals/check.rs420
-rw-r--r--src/internals/ctxt.rs62
-rw-r--r--src/internals/mod.rs24
-rw-r--r--src/internals/symbol.rs67
-rw-r--r--src/lib.rs98
-rw-r--r--src/pretend.rs140
-rw-r--r--src/ser.rs1334
-rw-r--r--src/try.rs24
28 files changed, 8546 insertions, 0 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
new file mode 100644
index 0000000..8e3161d
--- /dev/null
+++ b/.cargo_vcs_info.json
@@ -0,0 +1,5 @@
+{
+ "git": {
+ "sha1": "fc3f104c4a08a85b403c468348347240db6cc992"
+ }
+}
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..417079c
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,23 @@
+// This file is generated by cargo2android.py --run --device --dependencies.
+
+rust_proc_macro {
+ name: "libserde_derive",
+ crate_name: "serde_derive",
+ srcs: ["src/lib.rs"],
+ edition: "2015",
+ features: ["default"],
+ flags: [
+ "--cfg underscore_consts",
+ ],
+ rustlibs: [
+ "libproc_macro2",
+ "libquote",
+ "libsyn",
+ ],
+}
+
+// dependent_library ["feature_list"]
+// proc-macro2-1.0.24 "default,proc-macro"
+// quote-1.0.7 "default,proc-macro"
+// syn-1.0.51 "clone-impls,default,derive,parsing,printing,proc-macro,quote,visit"
+// unicode-xid-0.2.1 "default"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..6868925
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,45 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies
+#
+# If you believe there's an error in this file please file an
+# issue against the rust-lang/cargo repository. If you're
+# editing this file be aware that the upstream Cargo.toml
+# will likely look very different (and much more reasonable)
+
+[package]
+name = "serde_derive"
+version = "1.0.117"
+authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>", "David Tolnay <dtolnay@gmail.com>"]
+include = ["build.rs", "src/**/*.rs", "crates-io.md", "README.md", "LICENSE-APACHE", "LICENSE-MIT"]
+description = "Macros 1.1 implementation of #[derive(Serialize, Deserialize)]"
+homepage = "https://serde.rs"
+documentation = "https://serde.rs/derive.html"
+readme = "crates-io.md"
+keywords = ["serde", "serialization", "no_std"]
+license = "MIT OR Apache-2.0"
+repository = "https://github.com/serde-rs/serde"
+[package.metadata.docs.rs]
+targets = ["x86_64-unknown-linux-gnu"]
+
+[lib]
+name = "serde_derive"
+proc-macro = true
+[dependencies.proc-macro2]
+version = "1.0"
+
+[dependencies.quote]
+version = "1.0"
+
+[dependencies.syn]
+version = "1.0.33"
+features = ["visit"]
+[dev-dependencies.serde]
+version = "1.0"
+
+[features]
+default = []
+deserialize_in_place = []
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
new file mode 100644
index 0000000..4dfdd92
--- /dev/null
+++ b/Cargo.toml.orig
@@ -0,0 +1,31 @@
+[package]
+name = "serde_derive"
+version = "1.0.117" # remember to update html_root_url
+authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>", "David Tolnay <dtolnay@gmail.com>"]
+license = "MIT OR Apache-2.0"
+description = "Macros 1.1 implementation of #[derive(Serialize, Deserialize)]"
+homepage = "https://serde.rs"
+repository = "https://github.com/serde-rs/serde"
+documentation = "https://serde.rs/derive.html"
+keywords = ["serde", "serialization", "no_std"]
+readme = "crates-io.md"
+include = ["build.rs", "src/**/*.rs", "crates-io.md", "README.md", "LICENSE-APACHE", "LICENSE-MIT"]
+
+[features]
+default = []
+deserialize_in_place = []
+
+[lib]
+name = "serde_derive"
+proc-macro = true
+
+[dependencies]
+proc-macro2 = "1.0"
+quote = "1.0"
+syn = { version = "1.0.33", features = ["visit"] }
+
+[dev-dependencies]
+serde = { version = "1.0", path = "../serde" }
+
+[package.metadata.docs.rs]
+targets = ["x86_64-unknown-linux-gnu"]
diff --git a/LICENSE b/LICENSE
new file mode 120000
index 0000000..6b579aa
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1 @@
+LICENSE-APACHE \ No newline at end of file
diff --git a/LICENSE-APACHE b/LICENSE-APACHE
new file mode 100644
index 0000000..16fe87b
--- /dev/null
+++ b/LICENSE-APACHE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/LICENSE-MIT b/LICENSE-MIT
new file mode 100644
index 0000000..31aa793
--- /dev/null
+++ b/LICENSE-MIT
@@ -0,0 +1,23 @@
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..e2e7e84
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,19 @@
+name: "serde_derive"
+description: "Macros 1.1 implementation of #[derive(Serialize, Deserialize)]"
+third_party {
+ url {
+ type: HOMEPAGE
+ value: "https://crates.io/crates/serde_derive"
+ }
+ url {
+ type: ARCHIVE
+ value: "https://static.crates.io/crates/serde_derive/serde_derive-1.0.117.crate"
+ }
+ version: "1.0.117"
+ license_type: NOTICE
+ last_upgrade_date {
+ year: 2020
+ month: 11
+ day: 24
+ }
+}
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_APACHE2
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..46fc303
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1 @@
+include platform/prebuilts/rust:/OWNERS
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..14b0098
--- /dev/null
+++ b/README.md
@@ -0,0 +1,111 @@
+# Serde &emsp; [![Build Status]][actions] [![Latest Version]][crates.io] [![serde: rustc 1.13+]][Rust 1.13] [![serde_derive: rustc 1.31+]][Rust 1.31]
+
+[Build Status]: https://img.shields.io/github/workflow/status/serde-rs/serde/CI/master
+[actions]: https://github.com/serde-rs/serde/actions?query=branch%3Amaster
+[Latest Version]: https://img.shields.io/crates/v/serde.svg
+[crates.io]: https://crates.io/crates/serde
+[serde: rustc 1.13+]: https://img.shields.io/badge/serde-rustc_1.13+-lightgray.svg
+[serde_derive: rustc 1.31+]: https://img.shields.io/badge/serde_derive-rustc_1.31+-lightgray.svg
+[Rust 1.13]: https://blog.rust-lang.org/2016/11/10/Rust-1.13.html
+[Rust 1.31]: https://blog.rust-lang.org/2018/12/06/Rust-1.31-and-rust-2018.html
+
+**Serde is a framework for *ser*ializing and *de*serializing Rust data structures efficiently and generically.**
+
+---
+
+You may be looking for:
+
+- [An overview of Serde](https://serde.rs/)
+- [Data formats supported by Serde](https://serde.rs/#data-formats)
+- [Setting up `#[derive(Serialize, Deserialize)]`](https://serde.rs/derive.html)
+- [Examples](https://serde.rs/examples.html)
+- [API documentation](https://docs.serde.rs/serde/)
+- [Release notes](https://github.com/serde-rs/serde/releases)
+
+## Serde in action
+
+<details>
+<summary>
+Click to show Cargo.toml.
+<a href="https://play.rust-lang.org/?edition=2018&gist=72755f28f99afc95e01d63174b28c1f5" target="_blank">Run this code in the playground.</a>
+</summary>
+
+```toml
+[dependencies]
+
+# The core APIs, including the Serialize and Deserialize traits. Always
+# required when using Serde. The "derive" feature is only required when
+# using #[derive(Serialize, Deserialize)] to make Serde work with structs
+# and enums defined in your crate.
+serde = { version = "1.0", features = ["derive"] }
+
+# Each data format lives in its own crate; the sample code below uses JSON
+# but you may be using a different one.
+serde_json = "1.0"
+```
+
+</details>
+<p></p>
+
+```rust
+use serde::{Serialize, Deserialize};
+
+#[derive(Serialize, Deserialize, Debug)]
+struct Point {
+ x: i32,
+ y: i32,
+}
+
+fn main() {
+ let point = Point { x: 1, y: 2 };
+
+ // Convert the Point to a JSON string.
+ let serialized = serde_json::to_string(&point).unwrap();
+
+ // Prints serialized = {"x":1,"y":2}
+ println!("serialized = {}", serialized);
+
+ // Convert the JSON string back to a Point.
+ let deserialized: Point = serde_json::from_str(&serialized).unwrap();
+
+ // Prints deserialized = Point { x: 1, y: 2 }
+ println!("deserialized = {:?}", deserialized);
+}
+```
+
+## Getting help
+
+Serde is one of the most widely used Rust libraries so any place that Rustaceans
+congregate will be able to help you out. For chat, consider trying the
+[#general] or [#beginners] channels of the unofficial community Discord, the
+[#rust-usage] channel of the official Rust Project Discord, or the
+[#general][zulip] stream in Zulip. For asynchronous, consider the [\[rust\] tag
+on StackOverflow][stackoverflow], the [/r/rust] subreddit which has a pinned
+weekly easy questions post, or the Rust [Discourse forum][discourse]. It's
+acceptable to file a support issue in this repo but they tend not to get as many
+eyes as any of the above and may get closed without a response after some time.
+
+[#general]: https://discord.com/channels/273534239310479360/274215136414400513
+[#beginners]: https://discord.com/channels/273534239310479360/273541522815713281
+[#rust-usage]: https://discord.com/channels/442252698964721669/443150878111694848
+[zulip]: https://rust-lang.zulipchat.com/#narrow/stream/122651-general
+[stackoverflow]: https://stackoverflow.com/questions/tagged/rust
+[/r/rust]: https://www.reddit.com/r/rust
+[discourse]: https://users.rust-lang.org
+
+<br>
+
+#### License
+
+<sup>
+Licensed under either of <a href="LICENSE-APACHE">Apache License, Version
+2.0</a> or <a href="LICENSE-MIT">MIT license</a> at your option.
+</sup>
+
+<br>
+
+<sub>
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in Serde by you, as defined in the Apache-2.0 license, shall be
+dual licensed as above, without any additional terms or conditions.
+</sub>
diff --git a/build.rs b/build.rs
new file mode 100644
index 0000000..6e1b7b6
--- /dev/null
+++ b/build.rs
@@ -0,0 +1,30 @@
+use std::env;
+use std::process::Command;
+use std::str;
+
+// The rustc-cfg strings below are *not* public API. Please let us know by
+// opening a GitHub issue if your build environment requires some way to enable
+// these cfgs other than by executing our build script.
+fn main() {
+ let minor = match rustc_minor_version() {
+ Some(minor) => minor,
+ None => return,
+ };
+
+ // Underscore const names stabilized in Rust 1.37:
+ // https://blog.rust-lang.org/2019/08/15/Rust-1.37.0.html#using-unnamed-const-items-for-macros
+ if minor >= 37 {
+ println!("cargo:rustc-cfg=underscore_consts");
+ }
+}
+
+fn rustc_minor_version() -> Option<u32> {
+ let rustc = env::var_os("RUSTC")?;
+ let output = Command::new(rustc).arg("--version").output().ok()?;
+ let version = str::from_utf8(&output.stdout).ok()?;
+ let mut pieces = version.split('.');
+ if pieces.next() != Some("rustc 1") {
+ return None;
+ }
+ pieces.next()?.parse().ok()
+}
diff --git a/crates-io.md b/crates-io.md
new file mode 100644
index 0000000..0775761
--- /dev/null
+++ b/crates-io.md
@@ -0,0 +1,62 @@
+<!-- Serde readme rendered on crates.io -->
+
+**Serde is a framework for *ser*ializing and *de*serializing Rust data structures efficiently and generically.**
+
+---
+
+You may be looking for:
+
+- [An overview of Serde](https://serde.rs/)
+- [Data formats supported by Serde](https://serde.rs/#data-formats)
+- [Setting up `#[derive(Serialize, Deserialize)]`](https://serde.rs/derive.html)
+- [Examples](https://serde.rs/examples.html)
+- [API documentation](https://docs.serde.rs/serde/)
+- [Release notes](https://github.com/serde-rs/serde/releases)
+
+## Serde in action
+
+```rust
+use serde::{Serialize, Deserialize};
+
+#[derive(Serialize, Deserialize, Debug)]
+struct Point {
+ x: i32,
+ y: i32,
+}
+
+fn main() {
+ let point = Point { x: 1, y: 2 };
+
+ // Convert the Point to a JSON string.
+ let serialized = serde_json::to_string(&point).unwrap();
+
+ // Prints serialized = {"x":1,"y":2}
+ println!("serialized = {}", serialized);
+
+ // Convert the JSON string back to a Point.
+ let deserialized: Point = serde_json::from_str(&serialized).unwrap();
+
+ // Prints deserialized = Point { x: 1, y: 2 }
+ println!("deserialized = {:?}", deserialized);
+}
+```
+
+## Getting help
+
+Serde is one of the most widely used Rust libraries so any place that Rustaceans
+congregate will be able to help you out. For chat, consider trying the
+[#general] or [#beginners] channels of the unofficial community Discord, the
+[#rust-usage] channel of the official Rust Project Discord, or the
+[#general][zulip] stream in Zulip. For asynchronous, consider the [\[rust\] tag
+on StackOverflow][stackoverflow], the [/r/rust] subreddit which has a pinned
+weekly easy questions post, or the Rust [Discourse forum][discourse]. It's
+acceptable to file a support issue in this repo but they tend not to get as many
+eyes as any of the above and may get closed without a response after some time.
+
+[#general]: https://discord.com/channels/273534239310479360/274215136414400513
+[#beginners]: https://discord.com/channels/273534239310479360/273541522815713281
+[#rust-usage]: https://discord.com/channels/442252698964721669/443150878111694848
+[zulip]: https://rust-lang.zulipchat.com/#narrow/stream/122651-general
+[stackoverflow]: https://stackoverflow.com/questions/tagged/rust
+[/r/rust]: https://www.reddit.com/r/rust
+[discourse]: https://users.rust-lang.org
diff --git a/src/bound.rs b/src/bound.rs
new file mode 100644
index 0000000..ec2eafe
--- /dev/null
+++ b/src/bound.rs
@@ -0,0 +1,317 @@
+use std::collections::HashSet;
+
+use syn;
+use syn::punctuated::{Pair, Punctuated};
+use syn::visit::{self, Visit};
+
+use internals::ast::{Container, Data};
+use internals::{attr, ungroup};
+
+use proc_macro2::Span;
+
+// Remove the default from every type parameter because in the generated impls
+// they look like associated types: "error: associated type bindings are not
+// allowed here".
+pub fn without_defaults(generics: &syn::Generics) -> syn::Generics {
+ syn::Generics {
+ params: generics
+ .params
+ .iter()
+ .map(|param| match param {
+ syn::GenericParam::Type(param) => syn::GenericParam::Type(syn::TypeParam {
+ eq_token: None,
+ default: None,
+ ..param.clone()
+ }),
+ _ => param.clone(),
+ })
+ .collect(),
+ ..generics.clone()
+ }
+}
+
+pub fn with_where_predicates(
+ generics: &syn::Generics,
+ predicates: &[syn::WherePredicate],
+) -> syn::Generics {
+ let mut generics = generics.clone();
+ generics
+ .make_where_clause()
+ .predicates
+ .extend(predicates.iter().cloned());
+ generics
+}
+
+pub fn with_where_predicates_from_fields(
+ cont: &Container,
+ generics: &syn::Generics,
+ from_field: fn(&attr::Field) -> Option<&[syn::WherePredicate]>,
+) -> syn::Generics {
+ let predicates = cont
+ .data
+ .all_fields()
+ .flat_map(|field| from_field(&field.attrs))
+ .flat_map(|predicates| predicates.to_vec());
+
+ let mut generics = generics.clone();
+ generics.make_where_clause().predicates.extend(predicates);
+ generics
+}
+
+pub fn with_where_predicates_from_variants(
+ cont: &Container,
+ generics: &syn::Generics,
+ from_variant: fn(&attr::Variant) -> Option<&[syn::WherePredicate]>,
+) -> syn::Generics {
+ let variants = match &cont.data {
+ Data::Enum(variants) => variants,
+ Data::Struct(_, _) => {
+ return generics.clone();
+ }
+ };
+
+ let predicates = variants
+ .iter()
+ .flat_map(|variant| from_variant(&variant.attrs))
+ .flat_map(|predicates| predicates.to_vec());
+
+ let mut generics = generics.clone();
+ generics.make_where_clause().predicates.extend(predicates);
+ generics
+}
+
+// Puts the given bound on any generic type parameters that are used in fields
+// for which filter returns true.
+//
+// For example, the following struct needs the bound `A: Serialize, B:
+// Serialize`.
+//
+// struct S<'b, A, B: 'b, C> {
+// a: A,
+// b: Option<&'b B>
+// #[serde(skip_serializing)]
+// c: C,
+// }
+pub fn with_bound(
+ cont: &Container,
+ generics: &syn::Generics,
+ filter: fn(&attr::Field, Option<&attr::Variant>) -> bool,
+ bound: &syn::Path,
+) -> syn::Generics {
+ struct FindTyParams<'ast> {
+ // Set of all generic type parameters on the current struct (A, B, C in
+ // the example). Initialized up front.
+ all_type_params: HashSet<syn::Ident>,
+
+ // Set of generic type parameters used in fields for which filter
+ // returns true (A and B in the example). Filled in as the visitor sees
+ // them.
+ relevant_type_params: HashSet<syn::Ident>,
+
+ // Fields whose type is an associated type of one of the generic type
+ // parameters.
+ associated_type_usage: Vec<&'ast syn::TypePath>,
+ }
+ impl<'ast> Visit<'ast> for FindTyParams<'ast> {
+ fn visit_field(&mut self, field: &'ast syn::Field) {
+ if let syn::Type::Path(ty) = ungroup(&field.ty) {
+ if let Some(Pair::Punctuated(t, _)) = ty.path.segments.pairs().next() {
+ if self.all_type_params.contains(&t.ident) {
+ self.associated_type_usage.push(ty);
+ }
+ }
+ }
+ self.visit_type(&field.ty);
+ }
+
+ fn visit_path(&mut self, path: &'ast syn::Path) {
+ if let Some(seg) = path.segments.last() {
+ if seg.ident == "PhantomData" {
+ // Hardcoded exception, because PhantomData<T> implements
+ // Serialize and Deserialize whether or not T implements it.
+ return;
+ }
+ }
+ if path.leading_colon.is_none() && path.segments.len() == 1 {
+ let id = &path.segments[0].ident;
+ if self.all_type_params.contains(id) {
+ self.relevant_type_params.insert(id.clone());
+ }
+ }
+ visit::visit_path(self, path);
+ }
+
+ // Type parameter should not be considered used by a macro path.
+ //
+ // struct TypeMacro<T> {
+ // mac: T!(),
+ // marker: PhantomData<T>,
+ // }
+ fn visit_macro(&mut self, _mac: &'ast syn::Macro) {}
+ }
+
+ let all_type_params = generics
+ .type_params()
+ .map(|param| param.ident.clone())
+ .collect();
+
+ let mut visitor = FindTyParams {
+ all_type_params,
+ relevant_type_params: HashSet::new(),
+ associated_type_usage: Vec::new(),
+ };
+ match &cont.data {
+ Data::Enum(variants) => {
+ for variant in variants.iter() {
+ let relevant_fields = variant
+ .fields
+ .iter()
+ .filter(|field| filter(&field.attrs, Some(&variant.attrs)));
+ for field in relevant_fields {
+ visitor.visit_field(field.original);
+ }
+ }
+ }
+ Data::Struct(_, fields) => {
+ for field in fields.iter().filter(|field| filter(&field.attrs, None)) {
+ visitor.visit_field(field.original);
+ }
+ }
+ }
+
+ let relevant_type_params = visitor.relevant_type_params;
+ let associated_type_usage = visitor.associated_type_usage;
+ let new_predicates = generics
+ .type_params()
+ .map(|param| param.ident.clone())
+ .filter(|id| relevant_type_params.contains(id))
+ .map(|id| syn::TypePath {
+ qself: None,
+ path: id.into(),
+ })
+ .chain(associated_type_usage.into_iter().cloned())
+ .map(|bounded_ty| {
+ syn::WherePredicate::Type(syn::PredicateType {
+ lifetimes: None,
+ // the type parameter that is being bounded e.g. T
+ bounded_ty: syn::Type::Path(bounded_ty),
+ colon_token: <Token![:]>::default(),
+ // the bound e.g. Serialize
+ bounds: vec![syn::TypeParamBound::Trait(syn::TraitBound {
+ paren_token: None,
+ modifier: syn::TraitBoundModifier::None,
+ lifetimes: None,
+ path: bound.clone(),
+ })]
+ .into_iter()
+ .collect(),
+ })
+ });
+
+ let mut generics = generics.clone();
+ generics
+ .make_where_clause()
+ .predicates
+ .extend(new_predicates);
+ generics
+}
+
+pub fn with_self_bound(
+ cont: &Container,
+ generics: &syn::Generics,
+ bound: &syn::Path,
+) -> syn::Generics {
+ let mut generics = generics.clone();
+ generics
+ .make_where_clause()
+ .predicates
+ .push(syn::WherePredicate::Type(syn::PredicateType {
+ lifetimes: None,
+ // the type that is being bounded e.g. MyStruct<'a, T>
+ bounded_ty: type_of_item(cont),
+ colon_token: <Token![:]>::default(),
+ // the bound e.g. Default
+ bounds: vec![syn::TypeParamBound::Trait(syn::TraitBound {
+ paren_token: None,
+ modifier: syn::TraitBoundModifier::None,
+ lifetimes: None,
+ path: bound.clone(),
+ })]
+ .into_iter()
+ .collect(),
+ }));
+ generics
+}
+
+pub fn with_lifetime_bound(generics: &syn::Generics, lifetime: &str) -> syn::Generics {
+ let bound = syn::Lifetime::new(lifetime, Span::call_site());
+ let def = syn::LifetimeDef {
+ attrs: Vec::new(),
+ lifetime: bound.clone(),
+ colon_token: None,
+ bounds: Punctuated::new(),
+ };
+
+ let params = Some(syn::GenericParam::Lifetime(def))
+ .into_iter()
+ .chain(generics.params.iter().cloned().map(|mut param| {
+ match &mut param {
+ syn::GenericParam::Lifetime(param) => {
+ param.bounds.push(bound.clone());
+ }
+ syn::GenericParam::Type(param) => {
+ param
+ .bounds
+ .push(syn::TypeParamBound::Lifetime(bound.clone()));
+ }
+ syn::GenericParam::Const(_) => {}
+ }
+ param
+ }))
+ .collect();
+
+ syn::Generics {
+ params,
+ ..generics.clone()
+ }
+}
+
+fn type_of_item(cont: &Container) -> syn::Type {
+ syn::Type::Path(syn::TypePath {
+ qself: None,
+ path: syn::Path {
+ leading_colon: None,
+ segments: vec![syn::PathSegment {
+ ident: cont.ident.clone(),
+ arguments: syn::PathArguments::AngleBracketed(
+ syn::AngleBracketedGenericArguments {
+ colon2_token: None,
+ lt_token: <Token![<]>::default(),
+ args: cont
+ .generics
+ .params
+ .iter()
+ .map(|param| match param {
+ syn::GenericParam::Type(param) => {
+ syn::GenericArgument::Type(syn::Type::Path(syn::TypePath {
+ qself: None,
+ path: param.ident.clone().into(),
+ }))
+ }
+ syn::GenericParam::Lifetime(param) => {
+ syn::GenericArgument::Lifetime(param.lifetime.clone())
+ }
+ syn::GenericParam::Const(_) => {
+ panic!("Serde does not support const generics yet");
+ }
+ })
+ .collect(),
+ gt_token: <Token![>]>::default(),
+ },
+ ),
+ }]
+ .into_iter()
+ .collect(),
+ },
+ })
+}
diff --git a/src/de.rs b/src/de.rs
new file mode 100644
index 0000000..1f5733a
--- /dev/null
+++ b/src/de.rs
@@ -0,0 +1,3049 @@
+use proc_macro2::{Literal, Span, TokenStream};
+use quote::ToTokens;
+use syn::punctuated::Punctuated;
+use syn::spanned::Spanned;
+use syn::{self, Ident, Index, Member};
+
+use bound;
+use dummy;
+use fragment::{Expr, Fragment, Match, Stmts};
+use internals::ast::{Container, Data, Field, Style, Variant};
+use internals::{attr, ungroup, Ctxt, Derive};
+use pretend;
+
+use std::collections::BTreeSet;
+
+pub fn expand_derive_deserialize(input: &syn::DeriveInput) -> Result<TokenStream, Vec<syn::Error>> {
+ let ctxt = Ctxt::new();
+ let cont = match Container::from_ast(&ctxt, input, Derive::Deserialize) {
+ Some(cont) => cont,
+ None => return Err(ctxt.check().unwrap_err()),
+ };
+ precondition(&ctxt, &cont);
+ ctxt.check()?;
+
+ let ident = &cont.ident;
+ let params = Parameters::new(&cont);
+ let (de_impl_generics, _, ty_generics, where_clause) = split_with_de_lifetime(&params);
+ let body = Stmts(deserialize_body(&cont, &params));
+ let delife = params.borrowed.de_lifetime();
+ let serde = cont.attrs.serde_path();
+
+ let impl_block = if let Some(remote) = cont.attrs.remote() {
+ let vis = &input.vis;
+ let used = pretend::pretend_used(&cont);
+ quote! {
+ impl #de_impl_generics #ident #ty_generics #where_clause {
+ #vis fn deserialize<__D>(__deserializer: __D) -> #serde::export::Result<#remote #ty_generics, __D::Error>
+ where
+ __D: #serde::Deserializer<#delife>,
+ {
+ #used
+ #body
+ }
+ }
+ }
+ } else {
+ let fn_deserialize_in_place = deserialize_in_place_body(&cont, &params);
+
+ quote! {
+ #[automatically_derived]
+ impl #de_impl_generics #serde::Deserialize<#delife> for #ident #ty_generics #where_clause {
+ fn deserialize<__D>(__deserializer: __D) -> #serde::export::Result<Self, __D::Error>
+ where
+ __D: #serde::Deserializer<#delife>,
+ {
+ #body
+ }
+
+ #fn_deserialize_in_place
+ }
+ }
+ };
+
+ Ok(dummy::wrap_in_const(
+ cont.attrs.custom_serde_path(),
+ "DESERIALIZE",
+ ident,
+ impl_block,
+ ))
+}
+
+fn precondition(cx: &Ctxt, cont: &Container) {
+ precondition_sized(cx, cont);
+ precondition_no_de_lifetime(cx, cont);
+}
+
+fn precondition_sized(cx: &Ctxt, cont: &Container) {
+ if let Data::Struct(_, fields) = &cont.data {
+ if let Some(last) = fields.last() {
+ if let syn::Type::Slice(_) = ungroup(last.ty) {
+ cx.error_spanned_by(
+ cont.original,
+ "cannot deserialize a dynamically sized struct",
+ );
+ }
+ }
+ }
+}
+
+fn precondition_no_de_lifetime(cx: &Ctxt, cont: &Container) {
+ if let BorrowedLifetimes::Borrowed(_) = borrowed_lifetimes(cont) {
+ for param in cont.generics.lifetimes() {
+ if param.lifetime.to_string() == "'de" {
+ cx.error_spanned_by(
+ &param.lifetime,
+ "cannot deserialize when there is a lifetime parameter called 'de",
+ );
+ return;
+ }
+ }
+ }
+}
+
+struct Parameters {
+ /// Name of the type the `derive` is on.
+ local: syn::Ident,
+
+ /// Path to the type the impl is for. Either a single `Ident` for local
+ /// types or `some::remote::Ident` for remote types. Does not include
+ /// generic parameters.
+ this: syn::Path,
+
+ /// Generics including any explicit and inferred bounds for the impl.
+ generics: syn::Generics,
+
+ /// Lifetimes borrowed from the deserializer. These will become bounds on
+ /// the `'de` lifetime of the deserializer.
+ borrowed: BorrowedLifetimes,
+
+ /// At least one field has a serde(getter) attribute, implying that the
+ /// remote type has a private field.
+ has_getter: bool,
+}
+
+impl Parameters {
+ fn new(cont: &Container) -> Self {
+ let local = cont.ident.clone();
+ let this = match cont.attrs.remote() {
+ Some(remote) => remote.clone(),
+ None => cont.ident.clone().into(),
+ };
+ let borrowed = borrowed_lifetimes(cont);
+ let generics = build_generics(cont, &borrowed);
+ let has_getter = cont.data.has_getter();
+
+ Parameters {
+ local,
+ this,
+ generics,
+ borrowed,
+ has_getter,
+ }
+ }
+
+ /// Type name to use in error messages and `&'static str` arguments to
+ /// various Deserializer methods.
+ fn type_name(&self) -> String {
+ self.this.segments.last().unwrap().ident.to_string()
+ }
+}
+
+// All the generics in the input, plus a bound `T: Deserialize` for each generic
+// field type that will be deserialized by us, plus a bound `T: Default` for
+// each generic field type that will be set to a default value.
+fn build_generics(cont: &Container, borrowed: &BorrowedLifetimes) -> syn::Generics {
+ let generics = bound::without_defaults(cont.generics);
+
+ let generics = bound::with_where_predicates_from_fields(cont, &generics, attr::Field::de_bound);
+
+ let generics =
+ bound::with_where_predicates_from_variants(cont, &generics, attr::Variant::de_bound);
+
+ match cont.attrs.de_bound() {
+ Some(predicates) => bound::with_where_predicates(&generics, predicates),
+ None => {
+ let generics = match *cont.attrs.default() {
+ attr::Default::Default => {
+ bound::with_self_bound(cont, &generics, &parse_quote!(_serde::export::Default))
+ }
+ attr::Default::None | attr::Default::Path(_) => generics,
+ };
+
+ let delife = borrowed.de_lifetime();
+ let generics = bound::with_bound(
+ cont,
+ &generics,
+ needs_deserialize_bound,
+ &parse_quote!(_serde::Deserialize<#delife>),
+ );
+
+ bound::with_bound(
+ cont,
+ &generics,
+ requires_default,
+ &parse_quote!(_serde::export::Default),
+ )
+ }
+ }
+}
+
+// Fields with a `skip_deserializing` or `deserialize_with` attribute, or which
+// belong to a variant with a `skip_deserializing` or `deserialize_with`
+// attribute, are not deserialized by us so we do not generate a bound. Fields
+// with a `bound` attribute specify their own bound so we do not generate one.
+// All other fields may need a `T: Deserialize` bound where T is the type of the
+// field.
+fn needs_deserialize_bound(field: &attr::Field, variant: Option<&attr::Variant>) -> bool {
+ !field.skip_deserializing()
+ && field.deserialize_with().is_none()
+ && field.de_bound().is_none()
+ && variant.map_or(true, |variant| {
+ !variant.skip_deserializing()
+ && variant.deserialize_with().is_none()
+ && variant.de_bound().is_none()
+ })
+}
+
+// Fields with a `default` attribute (not `default=...`), and fields with a
+// `skip_deserializing` attribute that do not also have `default=...`.
+fn requires_default(field: &attr::Field, _variant: Option<&attr::Variant>) -> bool {
+ if let attr::Default::Default = *field.default() {
+ true
+ } else {
+ false
+ }
+}
+
+enum BorrowedLifetimes {
+ Borrowed(BTreeSet<syn::Lifetime>),
+ Static,
+}
+
+impl BorrowedLifetimes {
+ fn de_lifetime(&self) -> syn::Lifetime {
+ match *self {
+ BorrowedLifetimes::Borrowed(_) => syn::Lifetime::new("'de", Span::call_site()),
+ BorrowedLifetimes::Static => syn::Lifetime::new("'static", Span::call_site()),
+ }
+ }
+
+ fn de_lifetime_def(&self) -> Option<syn::LifetimeDef> {
+ match self {
+ BorrowedLifetimes::Borrowed(bounds) => Some(syn::LifetimeDef {
+ attrs: Vec::new(),
+ lifetime: syn::Lifetime::new("'de", Span::call_site()),
+ colon_token: None,
+ bounds: bounds.iter().cloned().collect(),
+ }),
+ BorrowedLifetimes::Static => None,
+ }
+ }
+}
+
+// The union of lifetimes borrowed by each field of the container.
+//
+// These turn into bounds on the `'de` lifetime of the Deserialize impl. If
+// lifetimes `'a` and `'b` are borrowed but `'c` is not, the impl is:
+//
+// impl<'de: 'a + 'b, 'a, 'b, 'c> Deserialize<'de> for S<'a, 'b, 'c>
+//
+// If any borrowed lifetime is `'static`, then `'de: 'static` would be redundant
+// and we use plain `'static` instead of `'de`.
+fn borrowed_lifetimes(cont: &Container) -> BorrowedLifetimes {
+ let mut lifetimes = BTreeSet::new();
+ for field in cont.data.all_fields() {
+ if !field.attrs.skip_deserializing() {
+ lifetimes.extend(field.attrs.borrowed_lifetimes().iter().cloned());
+ }
+ }
+ if lifetimes.iter().any(|b| b.to_string() == "'static") {
+ BorrowedLifetimes::Static
+ } else {
+ BorrowedLifetimes::Borrowed(lifetimes)
+ }
+}
+
+fn deserialize_body(cont: &Container, params: &Parameters) -> Fragment {
+ if cont.attrs.transparent() {
+ deserialize_transparent(cont, params)
+ } else if let Some(type_from) = cont.attrs.type_from() {
+ deserialize_from(type_from)
+ } else if let Some(type_try_from) = cont.attrs.type_try_from() {
+ deserialize_try_from(type_try_from)
+ } else if let attr::Identifier::No = cont.attrs.identifier() {
+ match &cont.data {
+ Data::Enum(variants) => deserialize_enum(params, variants, &cont.attrs),
+ Data::Struct(Style::Struct, fields) => {
+ deserialize_struct(None, params, fields, &cont.attrs, None, &Untagged::No)
+ }
+ Data::Struct(Style::Tuple, fields) | Data::Struct(Style::Newtype, fields) => {
+ deserialize_tuple(None, params, fields, &cont.attrs, None)
+ }
+ Data::Struct(Style::Unit, _) => deserialize_unit_struct(params, &cont.attrs),
+ }
+ } else {
+ match &cont.data {
+ Data::Enum(variants) => deserialize_custom_identifier(params, variants, &cont.attrs),
+ Data::Struct(_, _) => unreachable!("checked in serde_derive_internals"),
+ }
+ }
+}
+
+#[cfg(feature = "deserialize_in_place")]
+fn deserialize_in_place_body(cont: &Container, params: &Parameters) -> Option<Stmts> {
+ // Only remote derives have getters, and we do not generate
+ // deserialize_in_place for remote derives.
+ assert!(!params.has_getter);
+
+ if cont.attrs.transparent()
+ || cont.attrs.type_from().is_some()
+ || cont.attrs.type_try_from().is_some()
+ || cont.attrs.identifier().is_some()
+ || cont
+ .data
+ .all_fields()
+ .all(|f| f.attrs.deserialize_with().is_some())
+ {
+ return None;
+ }
+
+ let code = match &cont.data {
+ Data::Struct(Style::Struct, fields) => {
+ deserialize_struct_in_place(None, params, fields, &cont.attrs, None)?
+ }
+ Data::Struct(Style::Tuple, fields) | Data::Struct(Style::Newtype, fields) => {
+ deserialize_tuple_in_place(None, params, fields, &cont.attrs, None)
+ }
+ Data::Enum(_) | Data::Struct(Style::Unit, _) => {
+ return None;
+ }
+ };
+
+ let delife = params.borrowed.de_lifetime();
+ let stmts = Stmts(code);
+
+ let fn_deserialize_in_place = quote_block! {
+ fn deserialize_in_place<__D>(__deserializer: __D, __place: &mut Self) -> _serde::export::Result<(), __D::Error>
+ where
+ __D: _serde::Deserializer<#delife>,
+ {
+ #stmts
+ }
+ };
+
+ Some(Stmts(fn_deserialize_in_place))
+}
+
+#[cfg(not(feature = "deserialize_in_place"))]
+fn deserialize_in_place_body(_cont: &Container, _params: &Parameters) -> Option<Stmts> {
+ None
+}
+
+fn deserialize_transparent(cont: &Container, params: &Parameters) -> Fragment {
+ let fields = match &cont.data {
+ Data::Struct(_, fields) => fields,
+ Data::Enum(_) => unreachable!(),
+ };
+
+ let this = &params.this;
+ let transparent_field = fields.iter().find(|f| f.attrs.transparent()).unwrap();
+
+ let path = match transparent_field.attrs.deserialize_with() {
+ Some(path) => quote!(#path),
+ None => {
+ let span = transparent_field.original.span();
+ quote_spanned!(span=> _serde::Deserialize::deserialize)
+ }
+ };
+
+ let assign = fields.iter().map(|field| {
+ let member = &field.member;
+ if field as *const Field == transparent_field as *const Field {
+ quote!(#member: __transparent)
+ } else {
+ let value = match field.attrs.default() {
+ attr::Default::Default => quote!(_serde::export::Default::default()),
+ attr::Default::Path(path) => quote!(#path()),
+ attr::Default::None => quote!(_serde::export::PhantomData),
+ };
+ quote!(#member: #value)
+ }
+ });
+
+ quote_block! {
+ _serde::export::Result::map(
+ #path(__deserializer),
+ |__transparent| #this { #(#assign),* })
+ }
+}
+
+fn deserialize_from(type_from: &syn::Type) -> Fragment {
+ quote_block! {
+ _serde::export::Result::map(
+ <#type_from as _serde::Deserialize>::deserialize(__deserializer),
+ _serde::export::From::from)
+ }
+}
+
+fn deserialize_try_from(type_try_from: &syn::Type) -> Fragment {
+ quote_block! {
+ _serde::export::Result::and_then(
+ <#type_try_from as _serde::Deserialize>::deserialize(__deserializer),
+ |v| _serde::export::TryFrom::try_from(v).map_err(_serde::de::Error::custom))
+ }
+}
+
+fn deserialize_unit_struct(params: &Parameters, cattrs: &attr::Container) -> Fragment {
+ let this = &params.this;
+ let type_name = cattrs.name().deserialize_name();
+
+ let expecting = format!("unit struct {}", params.type_name());
+
+ quote_block! {
+ struct __Visitor;
+
+ impl<'de> _serde::de::Visitor<'de> for __Visitor {
+ type Value = #this;
+
+ fn expecting(&self, __formatter: &mut _serde::export::Formatter) -> _serde::export::fmt::Result {
+ _serde::export::Formatter::write_str(__formatter, #expecting)
+ }
+
+ #[inline]
+ fn visit_unit<__E>(self) -> _serde::export::Result<Self::Value, __E>
+ where
+ __E: _serde::de::Error,
+ {
+ _serde::export::Ok(#this)
+ }
+ }
+
+ _serde::Deserializer::deserialize_unit_struct(__deserializer, #type_name, __Visitor)
+ }
+}
+
+fn deserialize_tuple(
+ variant_ident: Option<&syn::Ident>,
+ params: &Parameters,
+ fields: &[Field],
+ cattrs: &attr::Container,
+ deserializer: Option<TokenStream>,
+) -> Fragment {
+ let this = &params.this;
+ let (de_impl_generics, de_ty_generics, ty_generics, where_clause) =
+ split_with_de_lifetime(params);
+ let delife = params.borrowed.de_lifetime();
+
+ assert!(!cattrs.has_flatten());
+
+ // If there are getters (implying private fields), construct the local type
+ // and use an `Into` conversion to get the remote type. If there are no
+ // getters then construct the target type directly.
+ let construct = if params.has_getter {
+ let local = &params.local;
+ quote!(#local)
+ } else {
+ quote!(#this)
+ };
+
+ let is_enum = variant_ident.is_some();
+ let type_path = match variant_ident {
+ Some(variant_ident) => quote!(#construct::#variant_ident),
+ None => construct,
+ };
+ let expecting = match variant_ident {
+ Some(variant_ident) => format!("tuple variant {}::{}", params.type_name(), variant_ident),
+ None => format!("tuple struct {}", params.type_name()),
+ };
+
+ let nfields = fields.len();
+
+ let visit_newtype_struct = if !is_enum && nfields == 1 {
+ Some(deserialize_newtype_struct(&type_path, params, &fields[0]))
+ } else {
+ None
+ };
+
+ let visit_seq = Stmts(deserialize_seq(
+ &type_path, params, fields, false, cattrs, &expecting,
+ ));
+
+ let visitor_expr = quote! {
+ __Visitor {
+ marker: _serde::export::PhantomData::<#this #ty_generics>,
+ lifetime: _serde::export::PhantomData,
+ }
+ };
+ let dispatch = if let Some(deserializer) = deserializer {
+ quote!(_serde::Deserializer::deserialize_tuple(#deserializer, #nfields, #visitor_expr))
+ } else if is_enum {
+ quote!(_serde::de::VariantAccess::tuple_variant(__variant, #nfields, #visitor_expr))
+ } else if nfields == 1 {
+ let type_name = cattrs.name().deserialize_name();
+ quote!(_serde::Deserializer::deserialize_newtype_struct(__deserializer, #type_name, #visitor_expr))
+ } else {
+ let type_name = cattrs.name().deserialize_name();
+ quote!(_serde::Deserializer::deserialize_tuple_struct(__deserializer, #type_name, #nfields, #visitor_expr))
+ };
+
+ let all_skipped = fields.iter().all(|field| field.attrs.skip_deserializing());
+ let visitor_var = if all_skipped {
+ quote!(_)
+ } else {
+ quote!(mut __seq)
+ };
+
+ quote_block! {
+ struct __Visitor #de_impl_generics #where_clause {
+ marker: _serde::export::PhantomData<#this #ty_generics>,
+ lifetime: _serde::export::PhantomData<&#delife ()>,
+ }
+
+ impl #de_impl_generics _serde::de::Visitor<#delife> for __Visitor #de_ty_generics #where_clause {
+ type Value = #this #ty_generics;
+
+ fn expecting(&self, __formatter: &mut _serde::export::Formatter) -> _serde::export::fmt::Result {
+ _serde::export::Formatter::write_str(__formatter, #expecting)
+ }
+
+ #visit_newtype_struct
+
+ #[inline]
+ fn visit_seq<__A>(self, #visitor_var: __A) -> _serde::export::Result<Self::Value, __A::Error>
+ where
+ __A: _serde::de::SeqAccess<#delife>,
+ {
+ #visit_seq
+ }
+ }
+
+ #dispatch
+ }
+}
+
+#[cfg(feature = "deserialize_in_place")]
+fn deserialize_tuple_in_place(
+ variant_ident: Option<syn::Ident>,
+ params: &Parameters,
+ fields: &[Field],
+ cattrs: &attr::Container,
+ deserializer: Option<TokenStream>,
+) -> Fragment {
+ let this = &params.this;
+ let (de_impl_generics, de_ty_generics, ty_generics, where_clause) =
+ split_with_de_lifetime(params);
+ let delife = params.borrowed.de_lifetime();
+
+ assert!(!cattrs.has_flatten());
+
+ let is_enum = variant_ident.is_some();
+ let expecting = match variant_ident {
+ Some(variant_ident) => format!("tuple variant {}::{}", params.type_name(), variant_ident),
+ None => format!("tuple struct {}", params.type_name()),
+ };
+
+ let nfields = fields.len();
+
+ let visit_newtype_struct = if !is_enum && nfields == 1 {
+ Some(deserialize_newtype_struct_in_place(params, &fields[0]))
+ } else {
+ None
+ };
+
+ let visit_seq = Stmts(deserialize_seq_in_place(params, fields, cattrs, &expecting));
+
+ let visitor_expr = quote! {
+ __Visitor {
+ place: __place,
+ lifetime: _serde::export::PhantomData,
+ }
+ };
+
+ let dispatch = if let Some(deserializer) = deserializer {
+ quote!(_serde::Deserializer::deserialize_tuple(#deserializer, #nfields, #visitor_expr))
+ } else if is_enum {
+ quote!(_serde::de::VariantAccess::tuple_variant(__variant, #nfields, #visitor_expr))
+ } else if nfields == 1 {
+ let type_name = cattrs.name().deserialize_name();
+ quote!(_serde::Deserializer::deserialize_newtype_struct(__deserializer, #type_name, #visitor_expr))
+ } else {
+ let type_name = cattrs.name().deserialize_name();
+ quote!(_serde::Deserializer::deserialize_tuple_struct(__deserializer, #type_name, #nfields, #visitor_expr))
+ };
+
+ let all_skipped = fields.iter().all(|field| field.attrs.skip_deserializing());
+ let visitor_var = if all_skipped {
+ quote!(_)
+ } else {
+ quote!(mut __seq)
+ };
+
+ let in_place_impl_generics = de_impl_generics.in_place();
+ let in_place_ty_generics = de_ty_generics.in_place();
+ let place_life = place_lifetime();
+
+ quote_block! {
+ struct __Visitor #in_place_impl_generics #where_clause {
+ place: &#place_life mut #this #ty_generics,
+ lifetime: _serde::export::PhantomData<&#delife ()>,
+ }
+
+ impl #in_place_impl_generics _serde::de::Visitor<#delife> for __Visitor #in_place_ty_generics #where_clause {
+ type Value = ();
+
+ fn expecting(&self, __formatter: &mut _serde::export::Formatter) -> _serde::export::fmt::Result {
+ _serde::export::Formatter::write_str(__formatter, #expecting)
+ }
+
+ #visit_newtype_struct
+
+ #[inline]
+ fn visit_seq<__A>(self, #visitor_var: __A) -> _serde::export::Result<Self::Value, __A::Error>
+ where
+ __A: _serde::de::SeqAccess<#delife>,
+ {
+ #visit_seq
+ }
+ }
+
+ #dispatch
+ }
+}
+
+fn deserialize_seq(
+ type_path: &TokenStream,
+ params: &Parameters,
+ fields: &[Field],
+ is_struct: bool,
+ cattrs: &attr::Container,
+ expecting: &str,
+) -> Fragment {
+ let vars = (0..fields.len()).map(field_i as fn(_) -> _);
+
+ let deserialized_count = fields
+ .iter()
+ .filter(|field| !field.attrs.skip_deserializing())
+ .count();
+ let expecting = if deserialized_count == 1 {
+ format!("{} with 1 element", expecting)
+ } else {
+ format!("{} with {} elements", expecting, deserialized_count)
+ };
+
+ let mut index_in_seq = 0_usize;
+ let let_values = vars.clone().zip(fields).map(|(var, field)| {
+ if field.attrs.skip_deserializing() {
+ let default = Expr(expr_is_missing(field, cattrs));
+ quote! {
+ let #var = #default;
+ }
+ } else {
+ let visit = match field.attrs.deserialize_with() {
+ None => {
+ let field_ty = field.ty;
+ let span = field.original.span();
+ let func =
+ quote_spanned!(span=> _serde::de::SeqAccess::next_element::<#field_ty>);
+ quote!(try!(#func(&mut __seq)))
+ }
+ Some(path) => {
+ let (wrapper, wrapper_ty) = wrap_deserialize_field_with(params, field.ty, path);
+ quote!({
+ #wrapper
+ _serde::export::Option::map(
+ try!(_serde::de::SeqAccess::next_element::<#wrapper_ty>(&mut __seq)),
+ |__wrap| __wrap.value)
+ })
+ }
+ };
+ let value_if_none = match field.attrs.default() {
+ attr::Default::Default => quote!(_serde::export::Default::default()),
+ attr::Default::Path(path) => quote!(#path()),
+ attr::Default::None => quote!(
+ return _serde::export::Err(_serde::de::Error::invalid_length(#index_in_seq, &#expecting));
+ ),
+ };
+ let assign = quote! {
+ let #var = match #visit {
+ _serde::export::Some(__value) => __value,
+ _serde::export::None => {
+ #value_if_none
+ }
+ };
+ };
+ index_in_seq += 1;
+ assign
+ }
+ });
+
+ let mut result = if is_struct {
+ let names = fields.iter().map(|f| &f.member);
+ quote! {
+ #type_path { #( #names: #vars ),* }
+ }
+ } else {
+ quote! {
+ #type_path ( #(#vars),* )
+ }
+ };
+
+ if params.has_getter {
+ let this = &params.this;
+ result = quote! {
+ _serde::export::Into::<#this>::into(#result)
+ };
+ }
+
+ let let_default = match cattrs.default() {
+ attr::Default::Default => Some(quote!(
+ let __default: Self::Value = _serde::export::Default::default();
+ )),
+ attr::Default::Path(path) => Some(quote!(
+ let __default: Self::Value = #path();
+ )),
+ attr::Default::None => {
+ // We don't need the default value, to prevent an unused variable warning
+ // we'll leave the line empty.
+ None
+ }
+ };
+
+ quote_block! {
+ #let_default
+ #(#let_values)*
+ _serde::export::Ok(#result)
+ }
+}
+
+#[cfg(feature = "deserialize_in_place")]
+fn deserialize_seq_in_place(
+ params: &Parameters,
+ fields: &[Field],
+ cattrs: &attr::Container,
+ expecting: &str,
+) -> Fragment {
+ let deserialized_count = fields
+ .iter()
+ .filter(|field| !field.attrs.skip_deserializing())
+ .count();
+ let expecting = if deserialized_count == 1 {
+ format!("{} with 1 element", expecting)
+ } else {
+ format!("{} with {} elements", expecting, deserialized_count)
+ };
+
+ let mut index_in_seq = 0usize;
+ let write_values = fields.iter().map(|field| {
+ let member = &field.member;
+
+ if field.attrs.skip_deserializing() {
+ let default = Expr(expr_is_missing(field, cattrs));
+ quote! {
+ self.place.#member = #default;
+ }
+ } else {
+ let value_if_none = match field.attrs.default() {
+ attr::Default::Default => quote!(
+ self.place.#member = _serde::export::Default::default();
+ ),
+ attr::Default::Path(path) => quote!(
+ self.place.#member = #path();
+ ),
+ attr::Default::None => quote!(
+ return _serde::export::Err(_serde::de::Error::invalid_length(#index_in_seq, &#expecting));
+ ),
+ };
+ let write = match field.attrs.deserialize_with() {
+ None => {
+ quote! {
+ if let _serde::export::None = try!(_serde::de::SeqAccess::next_element_seed(&mut __seq,
+ _serde::private::de::InPlaceSeed(&mut self.place.#member)))
+ {
+ #value_if_none
+ }
+ }
+ }
+ Some(path) => {
+ let (wrapper, wrapper_ty) = wrap_deserialize_field_with(params, field.ty, path);
+ quote!({
+ #wrapper
+ match try!(_serde::de::SeqAccess::next_element::<#wrapper_ty>(&mut __seq)) {
+ _serde::export::Some(__wrap) => {
+ self.place.#member = __wrap.value;
+ }
+ _serde::export::None => {
+ #value_if_none
+ }
+ }
+ })
+ }
+ };
+ index_in_seq += 1;
+ write
+ }
+ });
+
+ let this = &params.this;
+ let (_, ty_generics, _) = params.generics.split_for_impl();
+ let let_default = match cattrs.default() {
+ attr::Default::Default => Some(quote!(
+ let __default: #this #ty_generics = _serde::export::Default::default();
+ )),
+ attr::Default::Path(path) => Some(quote!(
+ let __default: #this #ty_generics = #path();
+ )),
+ attr::Default::None => {
+ // We don't need the default value, to prevent an unused variable warning
+ // we'll leave the line empty.
+ None
+ }
+ };
+
+ quote_block! {
+ #let_default
+ #(#write_values)*
+ _serde::export::Ok(())
+ }
+}
+
+fn deserialize_newtype_struct(
+ type_path: &TokenStream,
+ params: &Parameters,
+ field: &Field,
+) -> TokenStream {
+ let delife = params.borrowed.de_lifetime();
+ let field_ty = field.ty;
+
+ let value = match field.attrs.deserialize_with() {
+ None => {
+ let span = field.original.span();
+ let func = quote_spanned!(span=> <#field_ty as _serde::Deserialize>::deserialize);
+ quote! {
+ try!(#func(__e))
+ }
+ }
+ Some(path) => {
+ quote! {
+ try!(#path(__e))
+ }
+ }
+ };
+
+ let mut result = quote!(#type_path(__field0));
+ if params.has_getter {
+ let this = &params.this;
+ result = quote! {
+ _serde::export::Into::<#this>::into(#result)
+ };
+ }
+
+ quote! {
+ #[inline]
+ fn visit_newtype_struct<__E>(self, __e: __E) -> _serde::export::Result<Self::Value, __E::Error>
+ where
+ __E: _serde::Deserializer<#delife>,
+ {
+ let __field0: #field_ty = #value;
+ _serde::export::Ok(#result)
+ }
+ }
+}
+
+#[cfg(feature = "deserialize_in_place")]
+fn deserialize_newtype_struct_in_place(params: &Parameters, field: &Field) -> TokenStream {
+ // We do not generate deserialize_in_place if every field has a
+ // deserialize_with.
+ assert!(field.attrs.deserialize_with().is_none());
+
+ let delife = params.borrowed.de_lifetime();
+
+ quote! {
+ #[inline]
+ fn visit_newtype_struct<__E>(self, __e: __E) -> _serde::export::Result<Self::Value, __E::Error>
+ where
+ __E: _serde::Deserializer<#delife>,
+ {
+ _serde::Deserialize::deserialize_in_place(__e, &mut self.place.0)
+ }
+ }
+}
+
+enum Untagged {
+ Yes,
+ No,
+}
+
+fn deserialize_struct(
+ variant_ident: Option<&syn::Ident>,
+ params: &Parameters,
+ fields: &[Field],
+ cattrs: &attr::Container,
+ deserializer: Option<TokenStream>,
+ untagged: &Untagged,
+) -> Fragment {
+ let is_enum = variant_ident.is_some();
+
+ let this = &params.this;
+ let (de_impl_generics, de_ty_generics, ty_generics, where_clause) =
+ split_with_de_lifetime(params);
+ let delife = params.borrowed.de_lifetime();
+
+ // If there are getters (implying private fields), construct the local type
+ // and use an `Into` conversion to get the remote type. If there are no
+ // getters then construct the target type directly.
+ let construct = if params.has_getter {
+ let local = &params.local;
+ quote!(#local)
+ } else {
+ quote!(#this)
+ };
+
+ let type_path = match variant_ident {
+ Some(variant_ident) => quote!(#construct::#variant_ident),
+ None => construct,
+ };
+ let expecting = match variant_ident {
+ Some(variant_ident) => format!("struct variant {}::{}", params.type_name(), variant_ident),
+ None => format!("struct {}", params.type_name()),
+ };
+
+ let visit_seq = Stmts(deserialize_seq(
+ &type_path, params, fields, true, cattrs, &expecting,
+ ));
+
+ let (field_visitor, fields_stmt, visit_map) = if cattrs.has_flatten() {
+ deserialize_struct_as_map_visitor(&type_path, params, fields, cattrs)
+ } else {
+ deserialize_struct_as_struct_visitor(&type_path, params, fields, cattrs)
+ };
+ let field_visitor = Stmts(field_visitor);
+ let fields_stmt = fields_stmt.map(Stmts);
+ let visit_map = Stmts(visit_map);
+
+ let visitor_expr = quote! {
+ __Visitor {
+ marker: _serde::export::PhantomData::<#this #ty_generics>,
+ lifetime: _serde::export::PhantomData,
+ }
+ };
+ let dispatch = if let Some(deserializer) = deserializer {
+ quote! {
+ _serde::Deserializer::deserialize_any(#deserializer, #visitor_expr)
+ }
+ } else if is_enum && cattrs.has_flatten() {
+ quote! {
+ _serde::de::VariantAccess::newtype_variant_seed(__variant, #visitor_expr)
+ }
+ } else if is_enum {
+ quote! {
+ _serde::de::VariantAccess::struct_variant(__variant, FIELDS, #visitor_expr)
+ }
+ } else if cattrs.has_flatten() {
+ quote! {
+ _serde::Deserializer::deserialize_map(__deserializer, #visitor_expr)
+ }
+ } else {
+ let type_name = cattrs.name().deserialize_name();
+ quote! {
+ _serde::Deserializer::deserialize_struct(__deserializer, #type_name, FIELDS, #visitor_expr)
+ }
+ };
+
+ let all_skipped = fields.iter().all(|field| field.attrs.skip_deserializing());
+ let visitor_var = if all_skipped {
+ quote!(_)
+ } else {
+ quote!(mut __seq)
+ };
+
+ // 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 *untagged {
+ Untagged::No if !cattrs.has_flatten() => Some(quote! {
+ #[inline]
+ fn visit_seq<__A>(self, #visitor_var: __A) -> _serde::export::Result<Self::Value, __A::Error>
+ where
+ __A: _serde::de::SeqAccess<#delife>,
+ {
+ #visit_seq
+ }
+ }),
+ _ => None,
+ };
+
+ let visitor_seed = if is_enum && cattrs.has_flatten() {
+ Some(quote! {
+ impl #de_impl_generics _serde::de::DeserializeSeed<#delife> for __Visitor #de_ty_generics #where_clause {
+ type Value = #this #ty_generics;
+
+ fn deserialize<__D>(self, __deserializer: __D) -> _serde::export::Result<Self::Value, __D::Error>
+ where
+ __D: _serde::Deserializer<'de>,
+ {
+ _serde::Deserializer::deserialize_map(__deserializer, self)
+ }
+ }
+ })
+ } else {
+ None
+ };
+
+ quote_block! {
+ #field_visitor
+
+ struct __Visitor #de_impl_generics #where_clause {
+ marker: _serde::export::PhantomData<#this #ty_generics>,
+ lifetime: _serde::export::PhantomData<&#delife ()>,
+ }
+
+ impl #de_impl_generics _serde::de::Visitor<#delife> for __Visitor #de_ty_generics #where_clause {
+ type Value = #this #ty_generics;
+
+ fn expecting(&self, __formatter: &mut _serde::export::Formatter) -> _serde::export::fmt::Result {
+ _serde::export::Formatter::write_str(__formatter, #expecting)
+ }
+
+ #visit_seq
+
+ #[inline]
+ fn visit_map<__A>(self, mut __map: __A) -> _serde::export::Result<Self::Value, __A::Error>
+ where
+ __A: _serde::de::MapAccess<#delife>,
+ {
+ #visit_map
+ }
+ }
+
+ #visitor_seed
+
+ #fields_stmt
+
+ #dispatch
+ }
+}
+
+#[cfg(feature = "deserialize_in_place")]
+fn deserialize_struct_in_place(
+ variant_ident: Option<syn::Ident>,
+ params: &Parameters,
+ fields: &[Field],
+ cattrs: &attr::Container,
+ deserializer: Option<TokenStream>,
+) -> Option<Fragment> {
+ let is_enum = variant_ident.is_some();
+
+ // for now we do not support in_place deserialization for structs that
+ // are represented as map.
+ if cattrs.has_flatten() {
+ return None;
+ }
+
+ let this = &params.this;
+ let (de_impl_generics, de_ty_generics, ty_generics, where_clause) =
+ split_with_de_lifetime(params);
+ let delife = params.borrowed.de_lifetime();
+
+ let expecting = match variant_ident {
+ Some(variant_ident) => format!("struct variant {}::{}", params.type_name(), variant_ident),
+ None => format!("struct {}", params.type_name()),
+ };
+
+ let visit_seq = Stmts(deserialize_seq_in_place(params, fields, cattrs, &expecting));
+
+ let (field_visitor, fields_stmt, visit_map) =
+ deserialize_struct_as_struct_in_place_visitor(params, fields, cattrs);
+
+ let field_visitor = Stmts(field_visitor);
+ let fields_stmt = Stmts(fields_stmt);
+ let visit_map = Stmts(visit_map);
+
+ let visitor_expr = quote! {
+ __Visitor {
+ place: __place,
+ lifetime: _serde::export::PhantomData,
+ }
+ };
+ let dispatch = if let Some(deserializer) = deserializer {
+ quote! {
+ _serde::Deserializer::deserialize_any(#deserializer, #visitor_expr)
+ }
+ } else if is_enum {
+ quote! {
+ _serde::de::VariantAccess::struct_variant(__variant, FIELDS, #visitor_expr)
+ }
+ } else {
+ let type_name = cattrs.name().deserialize_name();
+ quote! {
+ _serde::Deserializer::deserialize_struct(__deserializer, #type_name, FIELDS, #visitor_expr)
+ }
+ };
+
+ let all_skipped = fields.iter().all(|field| field.attrs.skip_deserializing());
+ let visitor_var = if all_skipped {
+ quote!(_)
+ } else {
+ quote!(mut __seq)
+ };
+
+ let visit_seq = quote! {
+ #[inline]
+ fn visit_seq<__A>(self, #visitor_var: __A) -> _serde::export::Result<Self::Value, __A::Error>
+ where
+ __A: _serde::de::SeqAccess<#delife>,
+ {
+ #visit_seq
+ }
+ };
+
+ let in_place_impl_generics = de_impl_generics.in_place();
+ let in_place_ty_generics = de_ty_generics.in_place();
+ let place_life = place_lifetime();
+
+ Some(quote_block! {
+ #field_visitor
+
+ struct __Visitor #in_place_impl_generics #where_clause {
+ place: &#place_life mut #this #ty_generics,
+ lifetime: _serde::export::PhantomData<&#delife ()>,
+ }
+
+ impl #in_place_impl_generics _serde::de::Visitor<#delife> for __Visitor #in_place_ty_generics #where_clause {
+ type Value = ();
+
+ fn expecting(&self, __formatter: &mut _serde::export::Formatter) -> _serde::export::fmt::Result {
+ _serde::export::Formatter::write_str(__formatter, #expecting)
+ }
+
+ #visit_seq
+
+ #[inline]
+ fn visit_map<__A>(self, mut __map: __A) -> _serde::export::Result<Self::Value, __A::Error>
+ where
+ __A: _serde::de::MapAccess<#delife>,
+ {
+ #visit_map
+ }
+ }
+
+ #fields_stmt
+
+ #dispatch
+ })
+}
+
+fn deserialize_enum(
+ params: &Parameters,
+ variants: &[Variant],
+ cattrs: &attr::Container,
+) -> Fragment {
+ match cattrs.tag() {
+ attr::TagType::External => deserialize_externally_tagged_enum(params, variants, cattrs),
+ attr::TagType::Internal { tag } => {
+ deserialize_internally_tagged_enum(params, variants, cattrs, tag)
+ }
+ attr::TagType::Adjacent { tag, content } => {
+ deserialize_adjacently_tagged_enum(params, variants, cattrs, tag, content)
+ }
+ attr::TagType::None => deserialize_untagged_enum(params, variants, cattrs),
+ }
+}
+
+fn prepare_enum_variant_enum(
+ variants: &[Variant],
+ cattrs: &attr::Container,
+) -> (TokenStream, Stmts) {
+ let mut deserialized_variants = variants
+ .iter()
+ .enumerate()
+ .filter(|&(_, variant)| !variant.attrs.skip_deserializing());
+
+ let variant_names_idents: Vec<_> = deserialized_variants
+ .clone()
+ .map(|(i, variant)| {
+ (
+ variant.attrs.name().deserialize_name(),
+ field_i(i),
+ variant.attrs.aliases(),
+ )
+ })
+ .collect();
+
+ let other_idx = deserialized_variants.position(|(_, variant)| variant.attrs.other());
+
+ let variants_stmt = {
+ let variant_names = variant_names_idents.iter().map(|(name, _, _)| name);
+ quote! {
+ const VARIANTS: &'static [&'static str] = &[ #(#variant_names),* ];
+ }
+ };
+
+ let variant_visitor = Stmts(deserialize_generated_identifier(
+ &variant_names_idents,
+ cattrs,
+ true,
+ other_idx,
+ ));
+
+ (variants_stmt, variant_visitor)
+}
+
+fn deserialize_externally_tagged_enum(
+ params: &Parameters,
+ variants: &[Variant],
+ cattrs: &attr::Container,
+) -> Fragment {
+ let this = &params.this;
+ let (de_impl_generics, de_ty_generics, ty_generics, where_clause) =
+ split_with_de_lifetime(params);
+ let delife = params.borrowed.de_lifetime();
+
+ let type_name = cattrs.name().deserialize_name();
+ let expecting = format!("enum {}", params.type_name());
+
+ let (variants_stmt, variant_visitor) = prepare_enum_variant_enum(variants, cattrs);
+
+ // Match arms to extract a variant from a string
+ let variant_arms = variants
+ .iter()
+ .enumerate()
+ .filter(|&(_, variant)| !variant.attrs.skip_deserializing())
+ .map(|(i, variant)| {
+ let variant_name = field_i(i);
+
+ let block = Match(deserialize_externally_tagged_variant(
+ params, variant, cattrs,
+ ));
+
+ quote! {
+ (__Field::#variant_name, __variant) => #block
+ }
+ });
+
+ let all_skipped = variants
+ .iter()
+ .all(|variant| variant.attrs.skip_deserializing());
+ let match_variant = if all_skipped {
+ // This is an empty enum like `enum Impossible {}` or an enum in which
+ // all variants have `#[serde(skip_deserializing)]`.
+ quote! {
+ // FIXME: Once we drop support for Rust 1.15:
+ // let _serde::export::Err(__err) = _serde::de::EnumAccess::variant::<__Field>(__data);
+ // _serde::export::Err(__err)
+ _serde::export::Result::map(
+ _serde::de::EnumAccess::variant::<__Field>(__data),
+ |(__impossible, _)| match __impossible {})
+ }
+ } else {
+ quote! {
+ match try!(_serde::de::EnumAccess::variant(__data)) {
+ #(#variant_arms)*
+ }
+ }
+ };
+
+ quote_block! {
+ #variant_visitor
+
+ struct __Visitor #de_impl_generics #where_clause {
+ marker: _serde::export::PhantomData<#this #ty_generics>,
+ lifetime: _serde::export::PhantomData<&#delife ()>,
+ }
+
+ impl #de_impl_generics _serde::de::Visitor<#delife> for __Visitor #de_ty_generics #where_clause {
+ type Value = #this #ty_generics;
+
+ fn expecting(&self, __formatter: &mut _serde::export::Formatter) -> _serde::export::fmt::Result {
+ _serde::export::Formatter::write_str(__formatter, #expecting)
+ }
+
+ fn visit_enum<__A>(self, __data: __A) -> _serde::export::Result<Self::Value, __A::Error>
+ where
+ __A: _serde::de::EnumAccess<#delife>,
+ {
+ #match_variant
+ }
+ }
+
+ #variants_stmt
+
+ _serde::Deserializer::deserialize_enum(
+ __deserializer,
+ #type_name,
+ VARIANTS,
+ __Visitor {
+ marker: _serde::export::PhantomData::<#this #ty_generics>,
+ lifetime: _serde::export::PhantomData,
+ },
+ )
+ }
+}
+
+fn deserialize_internally_tagged_enum(
+ params: &Parameters,
+ variants: &[Variant],
+ cattrs: &attr::Container,
+ tag: &str,
+) -> Fragment {
+ let (variants_stmt, variant_visitor) = prepare_enum_variant_enum(variants, cattrs);
+
+ // Match arms to extract a variant from a string
+ let variant_arms = variants
+ .iter()
+ .enumerate()
+ .filter(|&(_, variant)| !variant.attrs.skip_deserializing())
+ .map(|(i, variant)| {
+ let variant_name = field_i(i);
+
+ let block = Match(deserialize_internally_tagged_variant(
+ params,
+ variant,
+ cattrs,
+ quote! {
+ _serde::private::de::ContentDeserializer::<__D::Error>::new(__tagged.content)
+ },
+ ));
+
+ quote! {
+ __Field::#variant_name => #block
+ }
+ });
+
+ quote_block! {
+ #variant_visitor
+
+ #variants_stmt
+
+ let __tagged = try!(_serde::Deserializer::deserialize_any(
+ __deserializer,
+ _serde::private::de::TaggedContentVisitor::<__Field>::new(#tag)));
+
+ match __tagged.tag {
+ #(#variant_arms)*
+ }
+ }
+}
+
+fn deserialize_adjacently_tagged_enum(
+ params: &Parameters,
+ variants: &[Variant],
+ cattrs: &attr::Container,
+ tag: &str,
+ content: &str,
+) -> Fragment {
+ let this = &params.this;
+ let (de_impl_generics, de_ty_generics, ty_generics, where_clause) =
+ split_with_de_lifetime(params);
+ let delife = params.borrowed.de_lifetime();
+
+ let (variants_stmt, variant_visitor) = prepare_enum_variant_enum(variants, cattrs);
+
+ let variant_arms: &Vec<_> = &variants
+ .iter()
+ .enumerate()
+ .filter(|&(_, variant)| !variant.attrs.skip_deserializing())
+ .map(|(i, variant)| {
+ let variant_index = field_i(i);
+
+ let block = Match(deserialize_untagged_variant(
+ params,
+ variant,
+ cattrs,
+ quote!(__deserializer),
+ ));
+
+ quote! {
+ __Field::#variant_index => #block
+ }
+ })
+ .collect();
+
+ let expecting = format!("adjacently tagged enum {}", params.type_name());
+ let type_name = cattrs.name().deserialize_name();
+ let deny_unknown_fields = cattrs.deny_unknown_fields();
+
+ // If unknown fields are allowed, we pick the visitor that can step over
+ // those. Otherwise we pick the visitor that fails on unknown keys.
+ let field_visitor_ty = if deny_unknown_fields {
+ quote! { _serde::private::de::TagOrContentFieldVisitor }
+ } else {
+ quote! { _serde::private::de::TagContentOtherFieldVisitor }
+ };
+
+ let tag_or_content = quote! {
+ #field_visitor_ty {
+ tag: #tag,
+ content: #content,
+ }
+ };
+
+ let mut missing_content = quote! {
+ _serde::export::Err(<__A::Error as _serde::de::Error>::missing_field(#content))
+ };
+ let mut missing_content_fallthrough = quote!();
+ let missing_content_arms = variants
+ .iter()
+ .enumerate()
+ .filter(|&(_, variant)| !variant.attrs.skip_deserializing())
+ .filter_map(|(i, variant)| {
+ let variant_index = field_i(i);
+ let variant_ident = &variant.ident;
+
+ let arm = match variant.style {
+ Style::Unit => quote! {
+ _serde::export::Ok(#this::#variant_ident)
+ },
+ Style::Newtype if variant.attrs.deserialize_with().is_none() => {
+ let span = variant.original.span();
+ let func = quote_spanned!(span=> _serde::private::de::missing_field);
+ quote! {
+ #func(#content).map(#this::#variant_ident)
+ }
+ }
+ _ => {
+ missing_content_fallthrough = quote!(_ => #missing_content);
+ return None;
+ }
+ };
+ Some(quote! {
+ __Field::#variant_index => #arm,
+ })
+ })
+ .collect::<Vec<_>>();
+ if !missing_content_arms.is_empty() {
+ missing_content = quote! {
+ match __field {
+ #(#missing_content_arms)*
+ #missing_content_fallthrough
+ }
+ };
+ }
+
+ // Advance the map by one key, returning early in case of error.
+ let next_key = quote! {
+ try!(_serde::de::MapAccess::next_key_seed(&mut __map, #tag_or_content))
+ };
+
+ // When allowing unknown fields, we want to transparently step through keys
+ // we don't care about until we find `tag`, `content`, or run out of keys.
+ let next_relevant_key = if deny_unknown_fields {
+ next_key
+ } else {
+ quote!({
+ let mut __rk : _serde::export::Option<_serde::private::de::TagOrContentField> = _serde::export::None;
+ while let _serde::export::Some(__k) = #next_key {
+ match __k {
+ _serde::private::de::TagContentOtherField::Other => {
+ try!(_serde::de::MapAccess::next_value::<_serde::de::IgnoredAny>(&mut __map));
+ continue;
+ },
+ _serde::private::de::TagContentOtherField::Tag => {
+ __rk = _serde::export::Some(_serde::private::de::TagOrContentField::Tag);
+ break;
+ }
+ _serde::private::de::TagContentOtherField::Content => {
+ __rk = _serde::export::Some(_serde::private::de::TagOrContentField::Content);
+ break;
+ }
+ }
+ }
+
+ __rk
+ })
+ };
+
+ // Step through remaining keys, looking for duplicates of previously-seen
+ // keys. When unknown fields are denied, any key that isn't a duplicate will
+ // at this point immediately produce an error.
+ let visit_remaining_keys = quote! {
+ match #next_relevant_key {
+ _serde::export::Some(_serde::private::de::TagOrContentField::Tag) => {
+ _serde::export::Err(<__A::Error as _serde::de::Error>::duplicate_field(#tag))
+ }
+ _serde::export::Some(_serde::private::de::TagOrContentField::Content) => {
+ _serde::export::Err(<__A::Error as _serde::de::Error>::duplicate_field(#content))
+ }
+ _serde::export::None => _serde::export::Ok(__ret),
+ }
+ };
+
+ let finish_content_then_tag = if variant_arms.is_empty() {
+ quote! {
+ match try!(_serde::de::MapAccess::next_value::<__Field>(&mut __map)) {}
+ }
+ } else {
+ quote! {
+ let __ret = try!(match try!(_serde::de::MapAccess::next_value(&mut __map)) {
+ // Deserialize the buffered content now that we know the variant.
+ #(#variant_arms)*
+ });
+ // Visit remaining keys, looking for duplicates.
+ #visit_remaining_keys
+ }
+ };
+
+ quote_block! {
+ #variant_visitor
+
+ #variants_stmt
+
+ struct __Seed #de_impl_generics #where_clause {
+ field: __Field,
+ marker: _serde::export::PhantomData<#this #ty_generics>,
+ lifetime: _serde::export::PhantomData<&#delife ()>,
+ }
+
+ impl #de_impl_generics _serde::de::DeserializeSeed<#delife> for __Seed #de_ty_generics #where_clause {
+ type Value = #this #ty_generics;
+
+ fn deserialize<__D>(self, __deserializer: __D) -> _serde::export::Result<Self::Value, __D::Error>
+ where
+ __D: _serde::Deserializer<#delife>,
+ {
+ match self.field {
+ #(#variant_arms)*
+ }
+ }
+ }
+
+ struct __Visitor #de_impl_generics #where_clause {
+ marker: _serde::export::PhantomData<#this #ty_generics>,
+ lifetime: _serde::export::PhantomData<&#delife ()>,
+ }
+
+ impl #de_impl_generics _serde::de::Visitor<#delife> for __Visitor #de_ty_generics #where_clause {
+ type Value = #this #ty_generics;
+
+ fn expecting(&self, __formatter: &mut _serde::export::Formatter) -> _serde::export::fmt::Result {
+ _serde::export::Formatter::write_str(__formatter, #expecting)
+ }
+
+ fn visit_map<__A>(self, mut __map: __A) -> _serde::export::Result<Self::Value, __A::Error>
+ where
+ __A: _serde::de::MapAccess<#delife>,
+ {
+ // Visit the first relevant key.
+ match #next_relevant_key {
+ // First key is the tag.
+ _serde::export::Some(_serde::private::de::TagOrContentField::Tag) => {
+ // Parse the tag.
+ let __field = try!(_serde::de::MapAccess::next_value(&mut __map));
+ // Visit the second key.
+ match #next_relevant_key {
+ // Second key is a duplicate of the tag.
+ _serde::export::Some(_serde::private::de::TagOrContentField::Tag) => {
+ _serde::export::Err(<__A::Error as _serde::de::Error>::duplicate_field(#tag))
+ }
+ // Second key is the content.
+ _serde::export::Some(_serde::private::de::TagOrContentField::Content) => {
+ let __ret = try!(_serde::de::MapAccess::next_value_seed(&mut __map,
+ __Seed {
+ field: __field,
+ marker: _serde::export::PhantomData,
+ lifetime: _serde::export::PhantomData,
+ }));
+ // Visit remaining keys, looking for duplicates.
+ #visit_remaining_keys
+ }
+ // There is no second key; might be okay if the we have a unit variant.
+ _serde::export::None => #missing_content
+ }
+ }
+ // First key is the content.
+ _serde::export::Some(_serde::private::de::TagOrContentField::Content) => {
+ // Buffer up the content.
+ let __content = try!(_serde::de::MapAccess::next_value::<_serde::private::de::Content>(&mut __map));
+ // Visit the second key.
+ match #next_relevant_key {
+ // Second key is the tag.
+ _serde::export::Some(_serde::private::de::TagOrContentField::Tag) => {
+ let __deserializer = _serde::private::de::ContentDeserializer::<__A::Error>::new(__content);
+ #finish_content_then_tag
+ }
+ // Second key is a duplicate of the content.
+ _serde::export::Some(_serde::private::de::TagOrContentField::Content) => {
+ _serde::export::Err(<__A::Error as _serde::de::Error>::duplicate_field(#content))
+ }
+ // There is no second key.
+ _serde::export::None => {
+ _serde::export::Err(<__A::Error as _serde::de::Error>::missing_field(#tag))
+ }
+ }
+ }
+ // There is no first key.
+ _serde::export::None => {
+ _serde::export::Err(<__A::Error as _serde::de::Error>::missing_field(#tag))
+ }
+ }
+ }
+
+ fn visit_seq<__A>(self, mut __seq: __A) -> _serde::export::Result<Self::Value, __A::Error>
+ where
+ __A: _serde::de::SeqAccess<#delife>,
+ {
+ // Visit the first element - the tag.
+ match try!(_serde::de::SeqAccess::next_element(&mut __seq)) {
+ _serde::export::Some(__field) => {
+ // Visit the second element - the content.
+ match try!(_serde::de::SeqAccess::next_element_seed(
+ &mut __seq,
+ __Seed {
+ field: __field,
+ marker: _serde::export::PhantomData,
+ lifetime: _serde::export::PhantomData,
+ },
+ )) {
+ _serde::export::Some(__ret) => _serde::export::Ok(__ret),
+ // There is no second element.
+ _serde::export::None => {
+ _serde::export::Err(_serde::de::Error::invalid_length(1, &self))
+ }
+ }
+ }
+ // There is no first element.
+ _serde::export::None => {
+ _serde::export::Err(_serde::de::Error::invalid_length(0, &self))
+ }
+ }
+ }
+ }
+
+ const FIELDS: &'static [&'static str] = &[#tag, #content];
+ _serde::Deserializer::deserialize_struct(
+ __deserializer,
+ #type_name,
+ FIELDS,
+ __Visitor {
+ marker: _serde::export::PhantomData::<#this #ty_generics>,
+ lifetime: _serde::export::PhantomData,
+ },
+ )
+ }
+}
+
+fn deserialize_untagged_enum(
+ params: &Parameters,
+ variants: &[Variant],
+ cattrs: &attr::Container,
+) -> Fragment {
+ let attempts = variants
+ .iter()
+ .filter(|variant| !variant.attrs.skip_deserializing())
+ .map(|variant| {
+ Expr(deserialize_untagged_variant(
+ params,
+ variant,
+ cattrs,
+ quote!(_serde::private::de::ContentRefDeserializer::<__D::Error>::new(&__content)),
+ ))
+ });
+
+ // TODO this message could be better by saving the errors from the failed
+ // attempts. The heuristic used by TOML was to count the number of fields
+ // processed before an error, and use the error that happened after the
+ // largest number of fields. I'm not sure I like that. Maybe it would be
+ // better to save all the errors and combine them into one message that
+ // explains why none of the variants matched.
+ let fallthrough_msg = format!(
+ "data did not match any variant of untagged enum {}",
+ params.type_name()
+ );
+
+ quote_block! {
+ let __content = try!(<_serde::private::de::Content as _serde::Deserialize>::deserialize(__deserializer));
+
+ #(
+ if let _serde::export::Ok(__ok) = #attempts {
+ return _serde::export::Ok(__ok);
+ }
+ )*
+
+ _serde::export::Err(_serde::de::Error::custom(#fallthrough_msg))
+ }
+}
+
+fn deserialize_externally_tagged_variant(
+ params: &Parameters,
+ variant: &Variant,
+ cattrs: &attr::Container,
+) -> Fragment {
+ if let Some(path) = variant.attrs.deserialize_with() {
+ let (wrapper, wrapper_ty, unwrap_fn) = wrap_deserialize_variant_with(params, variant, path);
+ return quote_block! {
+ #wrapper
+ _serde::export::Result::map(
+ _serde::de::VariantAccess::newtype_variant::<#wrapper_ty>(__variant), #unwrap_fn)
+ };
+ }
+
+ let variant_ident = &variant.ident;
+
+ match variant.style {
+ Style::Unit => {
+ let this = &params.this;
+ quote_block! {
+ try!(_serde::de::VariantAccess::unit_variant(__variant));
+ _serde::export::Ok(#this::#variant_ident)
+ }
+ }
+ Style::Newtype => deserialize_externally_tagged_newtype_variant(
+ variant_ident,
+ params,
+ &variant.fields[0],
+ cattrs,
+ ),
+ Style::Tuple => {
+ deserialize_tuple(Some(variant_ident), params, &variant.fields, cattrs, None)
+ }
+ Style::Struct => deserialize_struct(
+ Some(variant_ident),
+ params,
+ &variant.fields,
+ cattrs,
+ None,
+ &Untagged::No,
+ ),
+ }
+}
+
+fn deserialize_internally_tagged_variant(
+ params: &Parameters,
+ variant: &Variant,
+ cattrs: &attr::Container,
+ deserializer: TokenStream,
+) -> Fragment {
+ if variant.attrs.deserialize_with().is_some() {
+ return deserialize_untagged_variant(params, variant, cattrs, deserializer);
+ }
+
+ let variant_ident = &variant.ident;
+
+ match effective_style(variant) {
+ Style::Unit => {
+ let this = &params.this;
+ let type_name = params.type_name();
+ let variant_name = variant.ident.to_string();
+ let default = variant.fields.get(0).map(|field| {
+ let default = Expr(expr_is_missing(field, cattrs));
+ quote!((#default))
+ });
+ quote_block! {
+ try!(_serde::Deserializer::deserialize_any(#deserializer, _serde::private::de::InternallyTaggedUnitVisitor::new(#type_name, #variant_name)));
+ _serde::export::Ok(#this::#variant_ident #default)
+ }
+ }
+ Style::Newtype => deserialize_untagged_newtype_variant(
+ variant_ident,
+ params,
+ &variant.fields[0],
+ &deserializer,
+ ),
+ Style::Struct => deserialize_struct(
+ Some(variant_ident),
+ params,
+ &variant.fields,
+ cattrs,
+ Some(deserializer),
+ &Untagged::No,
+ ),
+ Style::Tuple => unreachable!("checked in serde_derive_internals"),
+ }
+}
+
+fn deserialize_untagged_variant(
+ params: &Parameters,
+ variant: &Variant,
+ cattrs: &attr::Container,
+ deserializer: TokenStream,
+) -> Fragment {
+ if let Some(path) = variant.attrs.deserialize_with() {
+ let (wrapper, wrapper_ty, unwrap_fn) = wrap_deserialize_variant_with(params, variant, path);
+ return quote_block! {
+ #wrapper
+ _serde::export::Result::map(
+ <#wrapper_ty as _serde::Deserialize>::deserialize(#deserializer), #unwrap_fn)
+ };
+ }
+
+ let variant_ident = &variant.ident;
+
+ match effective_style(variant) {
+ Style::Unit => {
+ let this = &params.this;
+ let type_name = params.type_name();
+ let variant_name = variant.ident.to_string();
+ let default = variant.fields.get(0).map(|field| {
+ let default = Expr(expr_is_missing(field, cattrs));
+ quote!((#default))
+ });
+ quote_expr! {
+ match _serde::Deserializer::deserialize_any(
+ #deserializer,
+ _serde::private::de::UntaggedUnitVisitor::new(#type_name, #variant_name)
+ ) {
+ _serde::export::Ok(()) => _serde::export::Ok(#this::#variant_ident #default),
+ _serde::export::Err(__err) => _serde::export::Err(__err),
+ }
+ }
+ }
+ Style::Newtype => deserialize_untagged_newtype_variant(
+ variant_ident,
+ params,
+ &variant.fields[0],
+ &deserializer,
+ ),
+ Style::Tuple => deserialize_tuple(
+ Some(variant_ident),
+ params,
+ &variant.fields,
+ cattrs,
+ Some(deserializer),
+ ),
+ Style::Struct => deserialize_struct(
+ Some(variant_ident),
+ params,
+ &variant.fields,
+ cattrs,
+ Some(deserializer),
+ &Untagged::Yes,
+ ),
+ }
+}
+
+fn deserialize_externally_tagged_newtype_variant(
+ variant_ident: &syn::Ident,
+ params: &Parameters,
+ field: &Field,
+ cattrs: &attr::Container,
+) -> Fragment {
+ let this = &params.this;
+
+ if field.attrs.skip_deserializing() {
+ let this = &params.this;
+ let default = Expr(expr_is_missing(field, cattrs));
+ return quote_block! {
+ try!(_serde::de::VariantAccess::unit_variant(__variant));
+ _serde::export::Ok(#this::#variant_ident(#default))
+ };
+ }
+
+ match field.attrs.deserialize_with() {
+ None => {
+ let field_ty = field.ty;
+ let span = field.original.span();
+ let func =
+ quote_spanned!(span=> _serde::de::VariantAccess::newtype_variant::<#field_ty>);
+ quote_expr! {
+ _serde::export::Result::map(#func(__variant), #this::#variant_ident)
+ }
+ }
+ Some(path) => {
+ let (wrapper, wrapper_ty) = wrap_deserialize_field_with(params, field.ty, path);
+ quote_block! {
+ #wrapper
+ _serde::export::Result::map(
+ _serde::de::VariantAccess::newtype_variant::<#wrapper_ty>(__variant),
+ |__wrapper| #this::#variant_ident(__wrapper.value))
+ }
+ }
+ }
+}
+
+fn deserialize_untagged_newtype_variant(
+ variant_ident: &syn::Ident,
+ params: &Parameters,
+ field: &Field,
+ deserializer: &TokenStream,
+) -> Fragment {
+ let this = &params.this;
+ let field_ty = field.ty;
+ match field.attrs.deserialize_with() {
+ None => {
+ let span = field.original.span();
+ let func = quote_spanned!(span=> <#field_ty as _serde::Deserialize>::deserialize);
+ quote_expr! {
+ _serde::export::Result::map(#func(#deserializer), #this::#variant_ident)
+ }
+ }
+ Some(path) => {
+ quote_block! {
+ let __value: _serde::export::Result<#field_ty, _> = #path(#deserializer);
+ _serde::export::Result::map(__value, #this::#variant_ident)
+ }
+ }
+ }
+}
+
+fn deserialize_generated_identifier(
+ fields: &[(String, Ident, Vec<String>)],
+ cattrs: &attr::Container,
+ is_variant: bool,
+ other_idx: Option<usize>,
+) -> Fragment {
+ let this = quote!(__Field);
+ let field_idents: &Vec<_> = &fields.iter().map(|(_, ident, _)| ident).collect();
+
+ 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))
+ } 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))
+ } 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))
+ };
+
+ let visitor_impl = Stmts(deserialize_identifier(
+ &this,
+ fields,
+ is_variant,
+ fallthrough,
+ !is_variant && cattrs.has_flatten(),
+ ));
+
+ let lifetime = if !is_variant && cattrs.has_flatten() {
+ Some(quote!(<'de>))
+ } else {
+ None
+ };
+
+ quote_block! {
+ #[allow(non_camel_case_types)]
+ enum __Field #lifetime {
+ #(#field_idents,)*
+ #ignore_variant
+ }
+
+ struct __FieldVisitor;
+
+ impl<'de> _serde::de::Visitor<'de> for __FieldVisitor {
+ type Value = __Field #lifetime;
+
+ #visitor_impl
+ }
+
+ impl<'de> _serde::Deserialize<'de> for __Field #lifetime {
+ #[inline]
+ fn deserialize<__D>(__deserializer: __D) -> _serde::export::Result<Self, __D::Error>
+ where
+ __D: _serde::Deserializer<'de>,
+ {
+ _serde::Deserializer::deserialize_identifier(__deserializer, __FieldVisitor)
+ }
+ }
+ }
+}
+
+fn deserialize_custom_identifier(
+ params: &Parameters,
+ variants: &[Variant],
+ cattrs: &attr::Container,
+) -> Fragment {
+ let is_variant = match cattrs.identifier() {
+ attr::Identifier::Variant => true,
+ attr::Identifier::Field => false,
+ attr::Identifier::No => unreachable!(),
+ };
+
+ let this = &params.this;
+ let this = quote!(#this);
+
+ let (ordinary, fallthrough) = if let Some(last) = variants.last() {
+ let last_ident = &last.ident;
+ if last.attrs.other() {
+ let ordinary = &variants[..variants.len() - 1];
+ let fallthrough = quote!(_serde::export::Ok(#this::#last_ident));
+ (ordinary, Some(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! {
+ _serde::export::Result::map(
+ _serde::Deserialize::deserialize(#deserializer),
+ #this::#last_ident)
+ };
+ (ordinary, Some(fallthrough))
+ } else {
+ (variants, None)
+ }
+ } else {
+ (variants, None)
+ };
+
+ let names_idents: Vec<_> = ordinary
+ .iter()
+ .map(|variant| {
+ (
+ variant.attrs.name().deserialize_name(),
+ variant.ident.clone(),
+ variant.attrs.aliases(),
+ )
+ })
+ .collect();
+
+ let names = names_idents.iter().map(|(name, _, _)| name);
+
+ let names_const = if fallthrough.is_some() {
+ None
+ } else if is_variant {
+ let variants = quote! {
+ const VARIANTS: &'static [&'static str] = &[ #(#names),* ];
+ };
+ Some(variants)
+ } else {
+ let fields = quote! {
+ const FIELDS: &'static [&'static str] = &[ #(#names),* ];
+ };
+ Some(fields)
+ };
+
+ let (de_impl_generics, de_ty_generics, ty_generics, where_clause) =
+ split_with_de_lifetime(params);
+ let delife = params.borrowed.de_lifetime();
+ let visitor_impl = Stmts(deserialize_identifier(
+ &this,
+ &names_idents,
+ is_variant,
+ fallthrough,
+ false,
+ ));
+
+ quote_block! {
+ #names_const
+
+ struct __FieldVisitor #de_impl_generics #where_clause {
+ marker: _serde::export::PhantomData<#this #ty_generics>,
+ lifetime: _serde::export::PhantomData<&#delife ()>,
+ }
+
+ impl #de_impl_generics _serde::de::Visitor<#delife> for __FieldVisitor #de_ty_generics #where_clause {
+ type Value = #this #ty_generics;
+
+ #visitor_impl
+ }
+
+ let __visitor = __FieldVisitor {
+ marker: _serde::export::PhantomData::<#this #ty_generics>,
+ lifetime: _serde::export::PhantomData,
+ };
+ _serde::Deserializer::deserialize_identifier(__deserializer, __visitor)
+ }
+}
+
+fn deserialize_identifier(
+ this: &TokenStream,
+ fields: &[(String, Ident, Vec<String>)],
+ is_variant: bool,
+ fallthrough: Option<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
+ .iter()
+ .map(|(name, _)| Literal::byte_string(name.as_bytes()));
+
+ let constructors: &Vec<_> = &flat_fields
+ .iter()
+ .map(|(_, ident)| quote!(#this::#ident))
+ .collect();
+ let main_constructors: &Vec<_> = &fields
+ .iter()
+ .map(|(_, ident, _)| quote!(#this::#ident))
+ .collect();
+
+ let expecting = if is_variant {
+ "variant identifier"
+ } else {
+ "field identifier"
+ };
+
+ let index_expecting = if is_variant { "variant" } else { "field" };
+
+ let bytes_to_str = if fallthrough.is_some() || collect_other_fields {
+ None
+ } else {
+ Some(quote! {
+ let __value = &_serde::export::from_utf8_lossy(__value);
+ })
+ };
+
+ let (
+ value_as_str_content,
+ value_as_borrowed_str_content,
+ value_as_bytes_content,
+ value_as_borrowed_bytes_content,
+ ) = if collect_other_fields {
+ (
+ Some(quote! {
+ let __value = _serde::private::de::Content::String(_serde::export::ToString::to_string(__value));
+ }),
+ Some(quote! {
+ let __value = _serde::private::de::Content::Str(__value);
+ }),
+ Some(quote! {
+ let __value = _serde::private::de::Content::ByteBuf(__value.to_vec());
+ }),
+ Some(quote! {
+ let __value = _serde::private::de::Content::Bytes(__value);
+ }),
+ )
+ } else {
+ (None, None, None, None)
+ };
+
+ let fallthrough_arm = if let Some(fallthrough) = fallthrough {
+ fallthrough
+ } else if is_variant {
+ quote! {
+ _serde::export::Err(_serde::de::Error::unknown_variant(__value, VARIANTS))
+ }
+ } else {
+ quote! {
+ _serde::export::Err(_serde::de::Error::unknown_field(__value, FIELDS))
+ }
+ };
+
+ let variant_indices = 0_u64..;
+ let fallthrough_msg = format!("{} index 0 <= i < {}", index_expecting, fields.len());
+ let visit_other = if collect_other_fields {
+ quote! {
+ fn visit_bool<__E>(self, __value: bool) -> _serde::export::Result<Self::Value, __E>
+ where
+ __E: _serde::de::Error,
+ {
+ _serde::export::Ok(__Field::__other(_serde::private::de::Content::Bool(__value)))
+ }
+
+ fn visit_i8<__E>(self, __value: i8) -> _serde::export::Result<Self::Value, __E>
+ where
+ __E: _serde::de::Error,
+ {
+ _serde::export::Ok(__Field::__other(_serde::private::de::Content::I8(__value)))
+ }
+
+ fn visit_i16<__E>(self, __value: i16) -> _serde::export::Result<Self::Value, __E>
+ where
+ __E: _serde::de::Error,
+ {
+ _serde::export::Ok(__Field::__other(_serde::private::de::Content::I16(__value)))
+ }
+
+ fn visit_i32<__E>(self, __value: i32) -> _serde::export::Result<Self::Value, __E>
+ where
+ __E: _serde::de::Error,
+ {
+ _serde::export::Ok(__Field::__other(_serde::private::de::Content::I32(__value)))
+ }
+
+ fn visit_i64<__E>(self, __value: i64) -> _serde::export::Result<Self::Value, __E>
+ where
+ __E: _serde::de::Error,
+ {
+ _serde::export::Ok(__Field::__other(_serde::private::de::Content::I64(__value)))
+ }
+
+ fn visit_u8<__E>(self, __value: u8) -> _serde::export::Result<Self::Value, __E>
+ where
+ __E: _serde::de::Error,
+ {
+ _serde::export::Ok(__Field::__other(_serde::private::de::Content::U8(__value)))
+ }
+
+ fn visit_u16<__E>(self, __value: u16) -> _serde::export::Result<Self::Value, __E>
+ where
+ __E: _serde::de::Error,
+ {
+ _serde::export::Ok(__Field::__other(_serde::private::de::Content::U16(__value)))
+ }
+
+ fn visit_u32<__E>(self, __value: u32) -> _serde::export::Result<Self::Value, __E>
+ where
+ __E: _serde::de::Error,
+ {
+ _serde::export::Ok(__Field::__other(_serde::private::de::Content::U32(__value)))
+ }
+
+ fn visit_u64<__E>(self, __value: u64) -> _serde::export::Result<Self::Value, __E>
+ where
+ __E: _serde::de::Error,
+ {
+ _serde::export::Ok(__Field::__other(_serde::private::de::Content::U64(__value)))
+ }
+
+ fn visit_f32<__E>(self, __value: f32) -> _serde::export::Result<Self::Value, __E>
+ where
+ __E: _serde::de::Error,
+ {
+ _serde::export::Ok(__Field::__other(_serde::private::de::Content::F32(__value)))
+ }
+
+ fn visit_f64<__E>(self, __value: f64) -> _serde::export::Result<Self::Value, __E>
+ where
+ __E: _serde::de::Error,
+ {
+ _serde::export::Ok(__Field::__other(_serde::private::de::Content::F64(__value)))
+ }
+
+ fn visit_char<__E>(self, __value: char) -> _serde::export::Result<Self::Value, __E>
+ where
+ __E: _serde::de::Error,
+ {
+ _serde::export::Ok(__Field::__other(_serde::private::de::Content::Char(__value)))
+ }
+
+ fn visit_unit<__E>(self) -> _serde::export::Result<Self::Value, __E>
+ where
+ __E: _serde::de::Error,
+ {
+ _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! {
+ fn visit_u64<__E>(self, __value: u64) -> _serde::export::Result<Self::Value, __E>
+ where
+ __E: _serde::de::Error,
+ {
+ match __value {
+ #(
+ #variant_indices => _serde::export::Ok(#main_constructors),
+ )*
+ _ => _serde::export::Err(_serde::de::Error::invalid_value(
+ _serde::de::Unexpected::Unsigned(__value),
+ &#fallthrough_msg,
+ ))
+ }
+ }
+ }
+ };
+
+ quote_block! {
+ fn expecting(&self, __formatter: &mut _serde::export::Formatter) -> _serde::export::fmt::Result {
+ _serde::export::Formatter::write_str(__formatter, #expecting)
+ }
+
+ #visit_other
+
+ fn visit_str<__E>(self, __value: &str) -> _serde::export::Result<Self::Value, __E>
+ where
+ __E: _serde::de::Error,
+ {
+ match __value {
+ #(
+ #field_strs => _serde::export::Ok(#constructors),
+ )*
+ _ => {
+ #value_as_str_content
+ #fallthrough_arm
+ }
+ }
+ }
+
+ fn visit_bytes<__E>(self, __value: &[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_bytes_content
+ #fallthrough_arm
+ }
+ }
+ }
+ }
+}
+
+fn deserialize_struct_as_struct_visitor(
+ struct_path: &TokenStream,
+ params: &Parameters,
+ fields: &[Field],
+ cattrs: &attr::Container,
+) -> (Fragment, Option<Fragment>, Fragment) {
+ assert!(!cattrs.has_flatten());
+
+ let field_names_idents: Vec<_> = fields
+ .iter()
+ .enumerate()
+ .filter(|&(_, field)| !field.attrs.skip_deserializing())
+ .map(|(i, field)| {
+ (
+ field.attrs.name().deserialize_name(),
+ field_i(i),
+ field.attrs.aliases(),
+ )
+ })
+ .collect();
+
+ let fields_stmt = {
+ let field_names = field_names_idents.iter().map(|(name, _, _)| name);
+ quote_block! {
+ const FIELDS: &'static [&'static str] = &[ #(#field_names),* ];
+ }
+ };
+
+ let field_visitor = deserialize_generated_identifier(&field_names_idents, cattrs, false, None);
+
+ let visit_map = deserialize_map(struct_path, params, fields, cattrs);
+
+ (field_visitor, Some(fields_stmt), visit_map)
+}
+
+fn deserialize_struct_as_map_visitor(
+ struct_path: &TokenStream,
+ params: &Parameters,
+ fields: &[Field],
+ cattrs: &attr::Container,
+) -> (Fragment, Option<Fragment>, Fragment) {
+ let field_names_idents: Vec<_> = fields
+ .iter()
+ .enumerate()
+ .filter(|&(_, field)| !field.attrs.skip_deserializing() && !field.attrs.flatten())
+ .map(|(i, field)| {
+ (
+ field.attrs.name().deserialize_name(),
+ field_i(i),
+ field.attrs.aliases(),
+ )
+ })
+ .collect();
+
+ let field_visitor = deserialize_generated_identifier(&field_names_idents, cattrs, false, None);
+
+ let visit_map = deserialize_map(struct_path, params, fields, cattrs);
+
+ (field_visitor, None, visit_map)
+}
+
+fn deserialize_map(
+ struct_path: &TokenStream,
+ params: &Parameters,
+ fields: &[Field],
+ cattrs: &attr::Container,
+) -> Fragment {
+ // Create the field names for the fields.
+ let fields_names: Vec<_> = fields
+ .iter()
+ .enumerate()
+ .map(|(i, field)| (field, field_i(i)))
+ .collect();
+
+ // Declare each field that will be deserialized.
+ let let_values = fields_names
+ .iter()
+ .filter(|&&(field, _)| !field.attrs.skip_deserializing() && !field.attrs.flatten())
+ .map(|(field, name)| {
+ let field_ty = field.ty;
+ quote! {
+ let mut #name: _serde::export::Option<#field_ty> = _serde::export::None;
+ }
+ });
+
+ // Collect contents for flatten fields into a buffer
+ let let_collect = if cattrs.has_flatten() {
+ Some(quote! {
+ let mut __collect = _serde::export::Vec::<_serde::export::Option<(
+ _serde::private::de::Content,
+ _serde::private::de::Content
+ )>>::new();
+ })
+ } else {
+ None
+ };
+
+ // Match arms to extract a value for a field.
+ let value_arms = fields_names
+ .iter()
+ .filter(|&&(field, _)| !field.attrs.skip_deserializing() && !field.attrs.flatten())
+ .map(|(field, name)| {
+ let deser_name = field.attrs.name().deserialize_name();
+
+ let visit = match field.attrs.deserialize_with() {
+ None => {
+ let field_ty = field.ty;
+ let span = field.original.span();
+ let func =
+ quote_spanned!(span=> _serde::de::MapAccess::next_value::<#field_ty>);
+ quote! {
+ try!(#func(&mut __map))
+ }
+ }
+ Some(path) => {
+ let (wrapper, wrapper_ty) = wrap_deserialize_field_with(params, field.ty, path);
+ quote!({
+ #wrapper
+ match _serde::de::MapAccess::next_value::<#wrapper_ty>(&mut __map) {
+ _serde::export::Ok(__wrapper) => __wrapper.value,
+ _serde::export::Err(__err) => {
+ return _serde::export::Err(__err);
+ }
+ }
+ })
+ }
+ };
+ quote! {
+ __Field::#name => {
+ if _serde::export::Option::is_some(&#name) {
+ return _serde::export::Err(<__A::Error as _serde::de::Error>::duplicate_field(#deser_name));
+ }
+ #name = _serde::export::Some(#visit);
+ }
+ }
+ });
+
+ // Visit ignored values to consume them
+ let ignored_arm = if cattrs.has_flatten() {
+ Some(quote! {
+ __Field::__other(__name) => {
+ __collect.push(_serde::export::Some((
+ __name,
+ try!(_serde::de::MapAccess::next_value(&mut __map)))));
+ }
+ })
+ } else if cattrs.deny_unknown_fields() {
+ None
+ } else {
+ Some(quote! {
+ _ => { let _ = try!(_serde::de::MapAccess::next_value::<_serde::de::IgnoredAny>(&mut __map)); }
+ })
+ };
+
+ let all_skipped = fields.iter().all(|field| field.attrs.skip_deserializing());
+ let match_keys = if cattrs.deny_unknown_fields() && all_skipped {
+ quote! {
+ // FIXME: Once we drop support for Rust 1.15:
+ // let _serde::export::None::<__Field> = try!(_serde::de::MapAccess::next_key(&mut __map));
+ _serde::export::Option::map(
+ try!(_serde::de::MapAccess::next_key::<__Field>(&mut __map)),
+ |__impossible| match __impossible {});
+ }
+ } else {
+ quote! {
+ while let _serde::export::Some(__key) = try!(_serde::de::MapAccess::next_key::<__Field>(&mut __map)) {
+ match __key {
+ #(#value_arms)*
+ #ignored_arm
+ }
+ }
+ }
+ };
+
+ let extract_values = fields_names
+ .iter()
+ .filter(|&&(field, _)| !field.attrs.skip_deserializing() && !field.attrs.flatten())
+ .map(|(field, name)| {
+ let missing_expr = Match(expr_is_missing(field, cattrs));
+
+ quote! {
+ let #name = match #name {
+ _serde::export::Some(#name) => #name,
+ _serde::export::None => #missing_expr
+ };
+ }
+ });
+
+ let extract_collected = fields_names
+ .iter()
+ .filter(|&&(field, _)| field.attrs.flatten() && !field.attrs.skip_deserializing())
+ .map(|(field, name)| {
+ let field_ty = field.ty;
+ let func = match field.attrs.deserialize_with() {
+ None => {
+ let span = field.original.span();
+ quote_spanned!(span=> _serde::de::Deserialize::deserialize)
+ }
+ Some(path) => quote!(#path),
+ };
+ quote! {
+ let #name: #field_ty = try!(#func(
+ _serde::private::de::FlatMapDeserializer(
+ &mut __collect,
+ _serde::export::PhantomData)));
+ }
+ });
+
+ let collected_deny_unknown_fields = if cattrs.has_flatten() && cattrs.deny_unknown_fields() {
+ Some(quote! {
+ if let _serde::export::Some(_serde::export::Some((__key, _))) =
+ __collect.into_iter().filter(_serde::export::Option::is_some).next()
+ {
+ if let _serde::export::Some(__key) = __key.as_str() {
+ return _serde::export::Err(
+ _serde::de::Error::custom(format_args!("unknown field `{}`", &__key)));
+ } else {
+ return _serde::export::Err(
+ _serde::de::Error::custom(format_args!("unexpected map key")));
+ }
+ }
+ })
+ } else {
+ None
+ };
+
+ let result = fields_names.iter().map(|(field, name)| {
+ let member = &field.member;
+ if field.attrs.skip_deserializing() {
+ let value = Expr(expr_is_missing(field, cattrs));
+ quote!(#member: #value)
+ } else {
+ quote!(#member: #name)
+ }
+ });
+
+ let let_default = match cattrs.default() {
+ attr::Default::Default => Some(quote!(
+ let __default: Self::Value = _serde::export::Default::default();
+ )),
+ attr::Default::Path(path) => Some(quote!(
+ let __default: Self::Value = #path();
+ )),
+ attr::Default::None => {
+ // We don't need the default value, to prevent an unused variable warning
+ // we'll leave the line empty.
+ None
+ }
+ };
+
+ let mut result = quote!(#struct_path { #(#result),* });
+ if params.has_getter {
+ let this = &params.this;
+ result = quote! {
+ _serde::export::Into::<#this>::into(#result)
+ };
+ }
+
+ quote_block! {
+ #(#let_values)*
+
+ #let_collect
+
+ #match_keys
+
+ #let_default
+
+ #(#extract_values)*
+
+ #(#extract_collected)*
+
+ #collected_deny_unknown_fields
+
+ _serde::export::Ok(#result)
+ }
+}
+
+#[cfg(feature = "deserialize_in_place")]
+fn deserialize_struct_as_struct_in_place_visitor(
+ params: &Parameters,
+ fields: &[Field],
+ cattrs: &attr::Container,
+) -> (Fragment, Fragment, Fragment) {
+ assert!(!cattrs.has_flatten());
+
+ let field_names_idents: Vec<_> = fields
+ .iter()
+ .enumerate()
+ .filter(|&(_, field)| !field.attrs.skip_deserializing())
+ .map(|(i, field)| {
+ (
+ field.attrs.name().deserialize_name(),
+ field_i(i),
+ field.attrs.aliases(),
+ )
+ })
+ .collect();
+
+ let fields_stmt = {
+ let field_names = field_names_idents.iter().map(|(name, _, _)| name);
+ quote_block! {
+ const FIELDS: &'static [&'static str] = &[ #(#field_names),* ];
+ }
+ };
+
+ let field_visitor = deserialize_generated_identifier(&field_names_idents, cattrs, false, None);
+
+ let visit_map = deserialize_map_in_place(params, fields, cattrs);
+
+ (field_visitor, fields_stmt, visit_map)
+}
+
+#[cfg(feature = "deserialize_in_place")]
+fn deserialize_map_in_place(
+ params: &Parameters,
+ fields: &[Field],
+ cattrs: &attr::Container,
+) -> Fragment {
+ assert!(!cattrs.has_flatten());
+
+ // Create the field names for the fields.
+ let fields_names: Vec<_> = fields
+ .iter()
+ .enumerate()
+ .map(|(i, field)| (field, field_i(i)))
+ .collect();
+
+ // For deserialize_in_place, declare booleans for each field that will be
+ // deserialized.
+ let let_flags = fields_names
+ .iter()
+ .filter(|&&(field, _)| !field.attrs.skip_deserializing())
+ .map(|(_, name)| {
+ quote! {
+ let mut #name: bool = false;
+ }
+ });
+
+ // Match arms to extract a value for a field.
+ let value_arms_from = fields_names
+ .iter()
+ .filter(|&&(field, _)| !field.attrs.skip_deserializing())
+ .map(|(field, name)| {
+ let deser_name = field.attrs.name().deserialize_name();
+ let member = &field.member;
+
+ let visit = match field.attrs.deserialize_with() {
+ None => {
+ quote! {
+ try!(_serde::de::MapAccess::next_value_seed(&mut __map, _serde::private::de::InPlaceSeed(&mut self.place.#member)))
+ }
+ }
+ Some(path) => {
+ let (wrapper, wrapper_ty) = wrap_deserialize_field_with(params, field.ty, path);
+ quote!({
+ #wrapper
+ self.place.#member = match _serde::de::MapAccess::next_value::<#wrapper_ty>(&mut __map) {
+ _serde::export::Ok(__wrapper) => __wrapper.value,
+ _serde::export::Err(__err) => {
+ return _serde::export::Err(__err);
+ }
+ };
+ })
+ }
+ };
+ quote! {
+ __Field::#name => {
+ if #name {
+ return _serde::export::Err(<__A::Error as _serde::de::Error>::duplicate_field(#deser_name));
+ }
+ #visit;
+ #name = true;
+ }
+ }
+ });
+
+ // Visit ignored values to consume them
+ let ignored_arm = if cattrs.deny_unknown_fields() {
+ None
+ } else {
+ Some(quote! {
+ _ => { let _ = try!(_serde::de::MapAccess::next_value::<_serde::de::IgnoredAny>(&mut __map)); }
+ })
+ };
+
+ let all_skipped = fields.iter().all(|field| field.attrs.skip_deserializing());
+
+ let match_keys = if cattrs.deny_unknown_fields() && all_skipped {
+ quote! {
+ // FIXME: Once we drop support for Rust 1.15:
+ // let _serde::export::None::<__Field> = try!(_serde::de::MapAccess::next_key(&mut __map));
+ _serde::export::Option::map(
+ try!(_serde::de::MapAccess::next_key::<__Field>(&mut __map)),
+ |__impossible| match __impossible {});
+ }
+ } else {
+ quote! {
+ while let _serde::export::Some(__key) = try!(_serde::de::MapAccess::next_key::<__Field>(&mut __map)) {
+ match __key {
+ #(#value_arms_from)*
+ #ignored_arm
+ }
+ }
+ }
+ };
+
+ let check_flags = fields_names
+ .iter()
+ .filter(|&&(field, _)| !field.attrs.skip_deserializing())
+ .map(|(field, name)| {
+ let missing_expr = expr_is_missing(field, cattrs);
+ // If missing_expr unconditionally returns an error, don't try
+ // to assign its value to self.place.
+ if field.attrs.default().is_none()
+ && cattrs.default().is_none()
+ && field.attrs.deserialize_with().is_some()
+ {
+ let missing_expr = Stmts(missing_expr);
+ quote! {
+ if !#name {
+ #missing_expr;
+ }
+ }
+ } else {
+ let member = &field.member;
+ let missing_expr = Expr(missing_expr);
+ quote! {
+ if !#name {
+ self.place.#member = #missing_expr;
+ };
+ }
+ }
+ });
+
+ let this = &params.this;
+ let (_, _, ty_generics, _) = split_with_de_lifetime(params);
+
+ let let_default = match cattrs.default() {
+ attr::Default::Default => Some(quote!(
+ let __default: #this #ty_generics = _serde::export::Default::default();
+ )),
+ attr::Default::Path(path) => Some(quote!(
+ let __default: #this #ty_generics = #path();
+ )),
+ attr::Default::None => {
+ // We don't need the default value, to prevent an unused variable warning
+ // we'll leave the line empty.
+ None
+ }
+ };
+
+ quote_block! {
+ #(#let_flags)*
+
+ #match_keys
+
+ #let_default
+
+ #(#check_flags)*
+
+ _serde::export::Ok(())
+ }
+}
+
+fn field_i(i: usize) -> Ident {
+ Ident::new(&format!("__field{}", i), Span::call_site())
+}
+
+/// This function wraps the expression in `#[serde(deserialize_with = "...")]`
+/// in a trait to prevent it from accessing the internal `Deserialize` state.
+fn wrap_deserialize_with(
+ params: &Parameters,
+ value_ty: &TokenStream,
+ deserialize_with: &syn::ExprPath,
+) -> (TokenStream, TokenStream) {
+ let this = &params.this;
+ let (de_impl_generics, de_ty_generics, ty_generics, where_clause) =
+ split_with_de_lifetime(params);
+ let delife = params.borrowed.de_lifetime();
+
+ let wrapper = quote! {
+ struct __DeserializeWith #de_impl_generics #where_clause {
+ value: #value_ty,
+ phantom: _serde::export::PhantomData<#this #ty_generics>,
+ lifetime: _serde::export::PhantomData<&#delife ()>,
+ }
+
+ impl #de_impl_generics _serde::Deserialize<#delife> for __DeserializeWith #de_ty_generics #where_clause {
+ fn deserialize<__D>(__deserializer: __D) -> _serde::export::Result<Self, __D::Error>
+ where
+ __D: _serde::Deserializer<#delife>,
+ {
+ _serde::export::Ok(__DeserializeWith {
+ value: try!(#deserialize_with(__deserializer)),
+ phantom: _serde::export::PhantomData,
+ lifetime: _serde::export::PhantomData,
+ })
+ }
+ }
+ };
+
+ let wrapper_ty = quote!(__DeserializeWith #de_ty_generics);
+
+ (wrapper, wrapper_ty)
+}
+
+fn wrap_deserialize_field_with(
+ params: &Parameters,
+ field_ty: &syn::Type,
+ deserialize_with: &syn::ExprPath,
+) -> (TokenStream, TokenStream) {
+ wrap_deserialize_with(params, &quote!(#field_ty), deserialize_with)
+}
+
+fn wrap_deserialize_variant_with(
+ params: &Parameters,
+ variant: &Variant,
+ deserialize_with: &syn::ExprPath,
+) -> (TokenStream, TokenStream, TokenStream) {
+ let this = &params.this;
+ let variant_ident = &variant.ident;
+
+ let field_tys = variant.fields.iter().map(|field| field.ty);
+ let (wrapper, wrapper_ty) =
+ wrap_deserialize_with(params, &quote!((#(#field_tys),*)), deserialize_with);
+
+ let field_access = (0..variant.fields.len()).map(|n| {
+ Member::Unnamed(Index {
+ index: n as u32,
+ span: Span::call_site(),
+ })
+ });
+ let unwrap_fn = match variant.style {
+ Style::Struct if variant.fields.len() == 1 => {
+ let member = &variant.fields[0].member;
+ quote! {
+ |__wrap| #this::#variant_ident { #member: __wrap.value }
+ }
+ }
+ Style::Struct => {
+ let members = variant.fields.iter().map(|field| &field.member);
+ quote! {
+ |__wrap| #this::#variant_ident { #(#members: __wrap.value.#field_access),* }
+ }
+ }
+ Style::Tuple => quote! {
+ |__wrap| #this::#variant_ident(#(__wrap.value.#field_access),*)
+ },
+ Style::Newtype => quote! {
+ |__wrap| #this::#variant_ident(__wrap.value)
+ },
+ Style::Unit => quote! {
+ |__wrap| #this::#variant_ident
+ },
+ };
+
+ (wrapper, wrapper_ty, unwrap_fn)
+}
+
+fn expr_is_missing(field: &Field, cattrs: &attr::Container) -> Fragment {
+ match field.attrs.default() {
+ attr::Default::Default => {
+ let span = field.original.span();
+ let func = quote_spanned!(span=> _serde::export::Default::default);
+ return quote_expr!(#func());
+ }
+ attr::Default::Path(path) => {
+ return quote_expr!(#path());
+ }
+ attr::Default::None => { /* below */ }
+ }
+
+ match *cattrs.default() {
+ attr::Default::Default | attr::Default::Path(_) => {
+ let member = &field.member;
+ return quote_expr!(__default.#member);
+ }
+ attr::Default::None => { /* below */ }
+ }
+
+ let name = field.attrs.name().deserialize_name();
+ match field.attrs.deserialize_with() {
+ None => {
+ let span = field.original.span();
+ let func = quote_spanned!(span=> _serde::private::de::missing_field);
+ quote_expr! {
+ try!(#func(#name))
+ }
+ }
+ Some(_) => {
+ quote_expr! {
+ return _serde::export::Err(<__A::Error as _serde::de::Error>::missing_field(#name))
+ }
+ }
+ }
+}
+
+fn effective_style(variant: &Variant) -> Style {
+ match variant.style {
+ Style::Newtype if variant.fields[0].attrs.skip_deserializing() => Style::Unit,
+ other => other,
+ }
+}
+
+struct DeImplGenerics<'a>(&'a Parameters);
+#[cfg(feature = "deserialize_in_place")]
+struct InPlaceImplGenerics<'a>(&'a Parameters);
+
+impl<'a> ToTokens for DeImplGenerics<'a> {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let mut generics = self.0.generics.clone();
+ if let Some(de_lifetime) = self.0.borrowed.de_lifetime_def() {
+ generics.params = Some(syn::GenericParam::Lifetime(de_lifetime))
+ .into_iter()
+ .chain(generics.params)
+ .collect();
+ }
+ let (impl_generics, _, _) = generics.split_for_impl();
+ impl_generics.to_tokens(tokens);
+ }
+}
+
+#[cfg(feature = "deserialize_in_place")]
+impl<'a> ToTokens for InPlaceImplGenerics<'a> {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let place_lifetime = place_lifetime();
+ let mut generics = self.0.generics.clone();
+
+ // Add lifetime for `&'place mut Self, and `'a: 'place`
+ for param in &mut generics.params {
+ match param {
+ syn::GenericParam::Lifetime(param) => {
+ param.bounds.push(place_lifetime.lifetime.clone());
+ }
+ syn::GenericParam::Type(param) => {
+ param.bounds.push(syn::TypeParamBound::Lifetime(
+ place_lifetime.lifetime.clone(),
+ ));
+ }
+ syn::GenericParam::Const(_) => {}
+ }
+ }
+ generics.params = Some(syn::GenericParam::Lifetime(place_lifetime))
+ .into_iter()
+ .chain(generics.params)
+ .collect();
+ if let Some(de_lifetime) = self.0.borrowed.de_lifetime_def() {
+ generics.params = Some(syn::GenericParam::Lifetime(de_lifetime))
+ .into_iter()
+ .chain(generics.params)
+ .collect();
+ }
+ let (impl_generics, _, _) = generics.split_for_impl();
+ impl_generics.to_tokens(tokens);
+ }
+}
+
+#[cfg(feature = "deserialize_in_place")]
+impl<'a> DeImplGenerics<'a> {
+ fn in_place(self) -> InPlaceImplGenerics<'a> {
+ InPlaceImplGenerics(self.0)
+ }
+}
+
+struct DeTypeGenerics<'a>(&'a Parameters);
+#[cfg(feature = "deserialize_in_place")]
+struct InPlaceTypeGenerics<'a>(&'a Parameters);
+
+impl<'a> ToTokens for DeTypeGenerics<'a> {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let mut generics = self.0.generics.clone();
+ if self.0.borrowed.de_lifetime_def().is_some() {
+ let def = syn::LifetimeDef {
+ attrs: Vec::new(),
+ lifetime: syn::Lifetime::new("'de", Span::call_site()),
+ colon_token: None,
+ bounds: Punctuated::new(),
+ };
+ generics.params = Some(syn::GenericParam::Lifetime(def))
+ .into_iter()
+ .chain(generics.params)
+ .collect();
+ }
+ let (_, ty_generics, _) = generics.split_for_impl();
+ ty_generics.to_tokens(tokens);
+ }
+}
+
+#[cfg(feature = "deserialize_in_place")]
+impl<'a> ToTokens for InPlaceTypeGenerics<'a> {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ let mut generics = self.0.generics.clone();
+ generics.params = Some(syn::GenericParam::Lifetime(place_lifetime()))
+ .into_iter()
+ .chain(generics.params)
+ .collect();
+
+ if self.0.borrowed.de_lifetime_def().is_some() {
+ let def = syn::LifetimeDef {
+ attrs: Vec::new(),
+ lifetime: syn::Lifetime::new("'de", Span::call_site()),
+ colon_token: None,
+ bounds: Punctuated::new(),
+ };
+ generics.params = Some(syn::GenericParam::Lifetime(def))
+ .into_iter()
+ .chain(generics.params)
+ .collect();
+ }
+ let (_, ty_generics, _) = generics.split_for_impl();
+ ty_generics.to_tokens(tokens);
+ }
+}
+
+#[cfg(feature = "deserialize_in_place")]
+impl<'a> DeTypeGenerics<'a> {
+ fn in_place(self) -> InPlaceTypeGenerics<'a> {
+ InPlaceTypeGenerics(self.0)
+ }
+}
+
+#[cfg(feature = "deserialize_in_place")]
+fn place_lifetime() -> syn::LifetimeDef {
+ syn::LifetimeDef {
+ attrs: Vec::new(),
+ lifetime: syn::Lifetime::new("'place", Span::call_site()),
+ colon_token: None,
+ bounds: Punctuated::new(),
+ }
+}
+
+fn split_with_de_lifetime(
+ params: &Parameters,
+) -> (
+ DeImplGenerics,
+ DeTypeGenerics,
+ syn::TypeGenerics,
+ Option<&syn::WhereClause>,
+) {
+ let de_impl_generics = DeImplGenerics(params);
+ let de_ty_generics = DeTypeGenerics(params);
+ let (_, ty_generics, where_clause) = params.generics.split_for_impl();
+ (de_impl_generics, de_ty_generics, ty_generics, where_clause)
+}
diff --git a/src/dummy.rs b/src/dummy.rs
new file mode 100644
index 0000000..9a4e5f0
--- /dev/null
+++ b/src/dummy.rs
@@ -0,0 +1,48 @@
+use proc_macro2::{Ident, TokenStream};
+use quote::format_ident;
+
+use syn;
+use try;
+
+pub fn wrap_in_const(
+ serde_path: Option<&syn::Path>,
+ trait_: &str,
+ ty: &Ident,
+ code: TokenStream,
+) -> TokenStream {
+ let try_replacement = try::replacement();
+
+ let dummy_const = if cfg!(underscore_consts) {
+ format_ident!("_")
+ } else {
+ format_ident!("_IMPL_{}_FOR_{}", trait_, unraw(ty))
+ };
+
+ let use_serde = match serde_path {
+ Some(path) => quote! {
+ use #path as _serde;
+ },
+ None => quote! {
+ #[allow(rust_2018_idioms, clippy::useless_attribute)]
+ extern crate serde as _serde;
+ },
+ };
+
+ quote! {
+ #[doc(hidden)]
+ #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
+ const #dummy_const: () = {
+ #use_serde
+ #try_replacement
+ #code
+ };
+ }
+}
+
+#[allow(deprecated)]
+fn unraw(ident: &Ident) -> String {
+ // str::trim_start_matches was added in 1.30, trim_left_matches deprecated
+ // in 1.33. We currently support rustc back to 1.15 so we need to continue
+ // to use the deprecated one.
+ ident.to_string().trim_left_matches("r#").to_owned()
+}
diff --git a/src/fragment.rs b/src/fragment.rs
new file mode 100644
index 0000000..324504a
--- /dev/null
+++ b/src/fragment.rs
@@ -0,0 +1,74 @@
+use proc_macro2::TokenStream;
+use quote::ToTokens;
+use syn::token;
+
+pub enum Fragment {
+ /// Tokens that can be used as an expression.
+ Expr(TokenStream),
+ /// Tokens that can be used inside a block. The surrounding curly braces are
+ /// not part of these tokens.
+ Block(TokenStream),
+}
+
+macro_rules! quote_expr {
+ ($($tt:tt)*) => {
+ $crate::fragment::Fragment::Expr(quote!($($tt)*))
+ }
+}
+
+macro_rules! quote_block {
+ ($($tt:tt)*) => {
+ $crate::fragment::Fragment::Block(quote!($($tt)*))
+ }
+}
+
+/// Interpolate a fragment in place of an expression. This involves surrounding
+/// Block fragments in curly braces.
+pub struct Expr(pub Fragment);
+impl ToTokens for Expr {
+ fn to_tokens(&self, out: &mut TokenStream) {
+ match &self.0 {
+ Fragment::Expr(expr) => expr.to_tokens(out),
+ Fragment::Block(block) => {
+ token::Brace::default().surround(out, |out| block.to_tokens(out));
+ }
+ }
+ }
+}
+
+/// Interpolate a fragment as the statements of a block.
+pub struct Stmts(pub Fragment);
+impl ToTokens for Stmts {
+ fn to_tokens(&self, out: &mut TokenStream) {
+ match &self.0 {
+ Fragment::Expr(expr) => expr.to_tokens(out),
+ Fragment::Block(block) => block.to_tokens(out),
+ }
+ }
+}
+
+/// Interpolate a fragment as the value part of a `match` expression. This
+/// involves putting a comma after expressions and curly braces around blocks.
+pub struct Match(pub Fragment);
+impl ToTokens for Match {
+ fn to_tokens(&self, out: &mut TokenStream) {
+ match &self.0 {
+ Fragment::Expr(expr) => {
+ expr.to_tokens(out);
+ <Token![,]>::default().to_tokens(out);
+ }
+ Fragment::Block(block) => {
+ token::Brace::default().surround(out, |out| block.to_tokens(out));
+ }
+ }
+ }
+}
+
+impl AsRef<TokenStream> for Fragment {
+ fn as_ref(&self) -> &TokenStream {
+ match self {
+ Fragment::Expr(expr) => expr,
+ Fragment::Block(block) => block,
+ }
+ }
+}
diff --git a/src/internals/ast.rs b/src/internals/ast.rs
new file mode 100644
index 0000000..1afdaee
--- /dev/null
+++ b/src/internals/ast.rs
@@ -0,0 +1,202 @@
+//! A Serde ast, parsed from the Syn ast and ready to generate Rust code.
+
+use internals::attr;
+use internals::check;
+use internals::{Ctxt, Derive};
+use syn;
+use syn::punctuated::Punctuated;
+
+/// A source data structure annotated with `#[derive(Serialize)]` and/or `#[derive(Deserialize)]`,
+/// parsed into an internal representation.
+pub struct Container<'a> {
+ /// The struct or enum name (without generics).
+ pub ident: syn::Ident,
+ /// Attributes on the structure, parsed for Serde.
+ pub attrs: attr::Container,
+ /// The contents of the struct or enum.
+ pub data: Data<'a>,
+ /// Any generics on the struct or enum.
+ pub generics: &'a syn::Generics,
+ /// Original input.
+ pub original: &'a syn::DeriveInput,
+}
+
+/// The fields of a struct or enum.
+///
+/// Analagous to `syn::Data`.
+pub enum Data<'a> {
+ Enum(Vec<Variant<'a>>),
+ Struct(Style, Vec<Field<'a>>),
+}
+
+/// A variant of an enum.
+pub struct Variant<'a> {
+ pub ident: syn::Ident,
+ pub attrs: attr::Variant,
+ pub style: Style,
+ pub fields: Vec<Field<'a>>,
+ pub original: &'a syn::Variant,
+}
+
+/// A field of a struct.
+pub struct Field<'a> {
+ pub member: syn::Member,
+ pub attrs: attr::Field,
+ pub ty: &'a syn::Type,
+ pub original: &'a syn::Field,
+}
+
+#[derive(Copy, Clone)]
+pub enum Style {
+ /// Named fields.
+ Struct,
+ /// Many unnamed fields.
+ Tuple,
+ /// One unnamed field.
+ Newtype,
+ /// No fields.
+ Unit,
+}
+
+impl<'a> Container<'a> {
+ /// Convert the raw Syn ast into a parsed container object, collecting errors in `cx`.
+ pub fn from_ast(
+ cx: &Ctxt,
+ item: &'a syn::DeriveInput,
+ derive: Derive,
+ ) -> Option<Container<'a>> {
+ let mut 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())),
+ syn::Data::Struct(data) => {
+ let (style, fields) = struct_from_ast(cx, &data.fields, None, attrs.default());
+ Data::Struct(style, fields)
+ }
+ syn::Data::Union(_) => {
+ cx.error_spanned_by(item, "Serde does not support derive for unions");
+ return None;
+ }
+ };
+
+ 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.rename_all_rules());
+ }
+ }
+ }
+ 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,
+ data,
+ generics: &item.generics,
+ original: item,
+ };
+ check::check(cx, &mut item, derive);
+ Some(item)
+ }
+}
+
+impl<'a> Data<'a> {
+ pub fn all_fields(&'a self) -> Box<Iterator<Item = &'a Field<'a>> + 'a> {
+ match self {
+ Data::Enum(variants) => {
+ Box::new(variants.iter().flat_map(|variant| variant.fields.iter()))
+ }
+ Data::Struct(_, fields) => Box::new(fields.iter()),
+ }
+ }
+
+ pub fn has_getter(&self) -> bool {
+ self.all_fields().any(|f| f.attrs.getter().is_some())
+ }
+}
+
+fn enum_from_ast<'a>(
+ cx: &Ctxt,
+ variants: &'a Punctuated<syn::Variant, Token![,]>,
+ container_default: &attr::Default,
+) -> Vec<Variant<'a>> {
+ variants
+ .iter()
+ .map(|variant| {
+ let attrs = attr::Variant::from_ast(cx, variant);
+ let (style, fields) =
+ struct_from_ast(cx, &variant.fields, Some(&attrs), container_default);
+ Variant {
+ ident: variant.ident.clone(),
+ attrs,
+ style,
+ fields,
+ original: variant,
+ }
+ })
+ .collect()
+}
+
+fn struct_from_ast<'a>(
+ cx: &Ctxt,
+ fields: &'a syn::Fields,
+ attrs: Option<&attr::Variant>,
+ container_default: &attr::Default,
+) -> (Style, Vec<Field<'a>>) {
+ match fields {
+ syn::Fields::Named(fields) => (
+ Style::Struct,
+ fields_from_ast(cx, &fields.named, attrs, container_default),
+ ),
+ syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => (
+ Style::Newtype,
+ fields_from_ast(cx, &fields.unnamed, attrs, container_default),
+ ),
+ syn::Fields::Unnamed(fields) => (
+ Style::Tuple,
+ fields_from_ast(cx, &fields.unnamed, attrs, container_default),
+ ),
+ syn::Fields::Unit => (Style::Unit, Vec::new()),
+ }
+}
+
+fn fields_from_ast<'a>(
+ cx: &Ctxt,
+ fields: &'a Punctuated<syn::Field, Token![,]>,
+ attrs: Option<&attr::Variant>,
+ container_default: &attr::Default,
+) -> Vec<Field<'a>> {
+ fields
+ .iter()
+ .enumerate()
+ .map(|(i, field)| Field {
+ member: match &field.ident {
+ Some(ident) => syn::Member::Named(ident.clone()),
+ None => syn::Member::Unnamed(i.into()),
+ },
+ attrs: attr::Field::from_ast(cx, i, field, attrs, container_default),
+ ty: &field.ty,
+ original: field,
+ })
+ .collect()
+}
diff --git a/src/internals/attr.rs b/src/internals/attr.rs
new file mode 100644
index 0000000..e6f72df
--- /dev/null
+++ b/src/internals/attr.rs
@@ -0,0 +1,1961 @@
+use internals::symbol::*;
+use internals::{ungroup, Ctxt};
+use proc_macro2::{Group, Span, TokenStream, TokenTree};
+use quote::ToTokens;
+use std::borrow::Cow;
+use std::collections::BTreeSet;
+use std::str::FromStr;
+use syn;
+use syn::parse::{self, Parse, ParseStream};
+use syn::punctuated::Punctuated;
+use syn::Ident;
+use syn::Meta::{List, NameValue, Path};
+use syn::NestedMeta::{Lit, Meta};
+
+// This module handles parsing of `#[serde(...)]` attributes. The entrypoints
+// are `attr::Container::from_ast`, `attr::Variant::from_ast`, and
+// `attr::Field::from_ast`. Each returns an instance of the corresponding
+// struct. Note that none of them return a Result. Unrecognized, malformed, or
+// duplicated attributes result in a span_err but otherwise are ignored. The
+// user will see errors simultaneously for all bad attributes in the crate
+// rather than just the first.
+
+pub use internals::case::RenameRule;
+
+struct Attr<'c, T> {
+ cx: &'c Ctxt,
+ name: Symbol,
+ tokens: TokenStream,
+ value: Option<T>,
+}
+
+impl<'c, T> Attr<'c, T> {
+ fn none(cx: &'c Ctxt, name: Symbol) -> Self {
+ Attr {
+ cx,
+ name,
+ tokens: TokenStream::new(),
+ value: None,
+ }
+ }
+
+ fn set<A: ToTokens>(&mut self, obj: A, value: T) {
+ let tokens = obj.into_token_stream();
+
+ if self.value.is_some() {
+ self.cx
+ .error_spanned_by(tokens, format!("duplicate serde attribute `{}`", self.name));
+ } else {
+ self.tokens = tokens;
+ self.value = Some(value);
+ }
+ }
+
+ fn set_opt<A: ToTokens>(&mut self, obj: A, value: Option<T>) {
+ if let Some(value) = value {
+ self.set(obj, value);
+ }
+ }
+
+ fn set_if_none(&mut self, value: T) {
+ if self.value.is_none() {
+ self.value = Some(value);
+ }
+ }
+
+ fn get(self) -> Option<T> {
+ self.value
+ }
+
+ fn get_with_tokens(self) -> Option<(TokenStream, T)> {
+ match self.value {
+ Some(v) => Some((self.tokens, v)),
+ None => None,
+ }
+ }
+}
+
+struct BoolAttr<'c>(Attr<'c, ()>);
+
+impl<'c> BoolAttr<'c> {
+ fn none(cx: &'c Ctxt, name: Symbol) -> Self {
+ BoolAttr(Attr::none(cx, name))
+ }
+
+ fn set_true<A: ToTokens>(&mut self, obj: A) {
+ self.0.set(obj, ());
+ }
+
+ fn get(&self) -> bool {
+ self.0.value.is_some()
+ }
+}
+
+struct VecAttr<'c, T> {
+ cx: &'c Ctxt,
+ name: Symbol,
+ first_dup_tokens: TokenStream,
+ values: Vec<T>,
+}
+
+impl<'c, T> VecAttr<'c, T> {
+ fn none(cx: &'c Ctxt, name: Symbol) -> Self {
+ VecAttr {
+ cx,
+ name,
+ first_dup_tokens: TokenStream::new(),
+ values: Vec::new(),
+ }
+ }
+
+ fn insert<A: ToTokens>(&mut self, obj: A, value: T) {
+ if self.values.len() == 1 {
+ self.first_dup_tokens = obj.into_token_stream();
+ }
+ self.values.push(value);
+ }
+
+ fn at_most_one(mut self) -> Result<Option<T>, ()> {
+ if self.values.len() > 1 {
+ let dup_token = self.first_dup_tokens;
+ self.cx.error_spanned_by(
+ dup_token,
+ format!("duplicate serde attribute `{}`", self.name),
+ );
+ Err(())
+ } else {
+ Ok(self.values.pop())
+ }
+ }
+
+ fn get(self) -> Vec<T> {
+ self.values
+ }
+}
+
+pub struct Name {
+ serialize: String,
+ serialize_renamed: bool,
+ deserialize: String,
+ deserialize_renamed: bool,
+ deserialize_aliases: Vec<String>,
+}
+
+#[allow(deprecated)]
+fn unraw(ident: &Ident) -> String {
+ // str::trim_start_matches was added in 1.30, trim_left_matches deprecated
+ // in 1.33. We currently support rustc back to 1.15 so we need to continue
+ // to use the deprecated one.
+ ident.to_string().trim_left_matches("r#").to_owned()
+}
+
+impl Name {
+ fn from_attrs(
+ source_name: String,
+ ser_name: Attr<String>,
+ de_name: Attr<String>,
+ de_aliases: Option<VecAttr<String>>,
+ ) -> Name {
+ let deserialize_aliases = match de_aliases {
+ Some(de_aliases) => {
+ let mut alias_list = BTreeSet::new();
+ for alias_name in de_aliases.get() {
+ alias_list.insert(alias_name);
+ }
+ alias_list.into_iter().collect()
+ }
+ None => Vec::new(),
+ };
+
+ let ser_name = ser_name.get();
+ let ser_renamed = ser_name.is_some();
+ let de_name = de_name.get();
+ let de_renamed = de_name.is_some();
+ Name {
+ serialize: ser_name.unwrap_or_else(|| source_name.clone()),
+ serialize_renamed: ser_renamed,
+ deserialize: de_name.unwrap_or(source_name),
+ deserialize_renamed: de_renamed,
+ deserialize_aliases,
+ }
+ }
+
+ /// Return the container name for the container when serializing.
+ pub fn serialize_name(&self) -> String {
+ self.serialize.clone()
+ }
+
+ /// Return the container name for the container when deserializing.
+ pub fn deserialize_name(&self) -> String {
+ self.deserialize.clone()
+ }
+
+ fn deserialize_aliases(&self) -> Vec<String> {
+ let mut aliases = self.deserialize_aliases.clone();
+ let main_name = self.deserialize_name();
+ if !aliases.contains(&main_name) {
+ aliases.push(main_name);
+ }
+ aliases
+ }
+}
+
+pub struct RenameAllRules {
+ serialize: RenameRule,
+ deserialize: RenameRule,
+}
+
+/// Represents struct or enum attribute information.
+pub struct Container {
+ name: Name,
+ transparent: bool,
+ deny_unknown_fields: bool,
+ default: Default,
+ rename_all_rules: RenameAllRules,
+ ser_bound: Option<Vec<syn::WherePredicate>>,
+ de_bound: Option<Vec<syn::WherePredicate>>,
+ tag: TagType,
+ type_from: Option<syn::Type>,
+ type_try_from: Option<syn::Type>,
+ type_into: Option<syn::Type>,
+ remote: Option<syn::Path>,
+ identifier: Identifier,
+ has_flatten: bool,
+ serde_path: Option<syn::Path>,
+ is_packed: bool,
+}
+
+/// Styles of representing an enum.
+pub enum TagType {
+ /// The default.
+ ///
+ /// ```json
+ /// {"variant1": {"key1": "value1", "key2": "value2"}}
+ /// ```
+ External,
+
+ /// `#[serde(tag = "type")]`
+ ///
+ /// ```json
+ /// {"type": "variant1", "key1": "value1", "key2": "value2"}
+ /// ```
+ Internal { tag: String },
+
+ /// `#[serde(tag = "t", content = "c")]`
+ ///
+ /// ```json
+ /// {"t": "variant1", "c": {"key1": "value1", "key2": "value2"}}
+ /// ```
+ Adjacent { tag: String, content: String },
+
+ /// `#[serde(untagged)]`
+ ///
+ /// ```json
+ /// {"key1": "value1", "key2": "value2"}
+ /// ```
+ None,
+}
+
+/// Whether this enum represents the fields of a struct or the variants of an
+/// enum.
+#[derive(Copy, Clone)]
+pub enum Identifier {
+ /// It does not.
+ No,
+
+ /// This enum represents the fields of a struct. All of the variants must be
+ /// unit variants, except possibly one which is annotated with
+ /// `#[serde(other)]` and is a newtype variant.
+ Field,
+
+ /// This enum represents the variants of an enum. All of the variants must
+ /// be unit variants.
+ Variant,
+}
+
+impl Identifier {
+ #[cfg(feature = "deserialize_in_place")]
+ pub fn is_some(self) -> bool {
+ match self {
+ Identifier::No => false,
+ Identifier::Field | Identifier::Variant => true,
+ }
+ }
+}
+
+impl Container {
+ /// Extract out the `#[serde(...)]` attributes from an item.
+ pub fn from_ast(cx: &Ctxt, item: &syn::DeriveInput) -> Self {
+ let mut ser_name = Attr::none(cx, RENAME);
+ let mut de_name = Attr::none(cx, RENAME);
+ let mut transparent = BoolAttr::none(cx, TRANSPARENT);
+ let mut deny_unknown_fields = BoolAttr::none(cx, DENY_UNKNOWN_FIELDS);
+ let mut default = Attr::none(cx, DEFAULT);
+ let mut rename_all_ser_rule = Attr::none(cx, RENAME_ALL);
+ let mut rename_all_de_rule = Attr::none(cx, RENAME_ALL);
+ let mut ser_bound = Attr::none(cx, BOUND);
+ let mut de_bound = Attr::none(cx, BOUND);
+ let mut untagged = BoolAttr::none(cx, UNTAGGED);
+ let mut internal_tag = Attr::none(cx, TAG);
+ let mut content = Attr::none(cx, CONTENT);
+ let mut type_from = Attr::none(cx, FROM);
+ let mut type_try_from = Attr::none(cx, TRY_FROM);
+ let mut type_into = Attr::none(cx, INTO);
+ let mut remote = Attr::none(cx, REMOTE);
+ let mut field_identifier = BoolAttr::none(cx, FIELD_IDENTIFIER);
+ let mut variant_identifier = BoolAttr::none(cx, VARIANT_IDENTIFIER);
+ let mut serde_path = Attr::none(cx, CRATE);
+
+ for meta_item in item
+ .attrs
+ .iter()
+ .flat_map(|attr| get_serde_meta_items(cx, attr))
+ .flatten()
+ {
+ match &meta_item {
+ // Parse `#[serde(rename = "foo")]`
+ Meta(NameValue(m)) if m.path == RENAME => {
+ if let Ok(s) = get_lit_str(cx, RENAME, &m.lit) {
+ ser_name.set(&m.path, s.value());
+ de_name.set(&m.path, s.value());
+ }
+ }
+
+ // Parse `#[serde(rename(serialize = "foo", deserialize = "bar"))]`
+ Meta(List(m)) if m.path == RENAME => {
+ if let Ok((ser, de)) = get_renames(cx, &m.nested) {
+ ser_name.set_opt(&m.path, ser.map(syn::LitStr::value));
+ de_name.set_opt(&m.path, de.map(syn::LitStr::value));
+ }
+ }
+
+ // Parse `#[serde(rename_all = "foo")]`
+ Meta(NameValue(m)) if m.path == RENAME_ALL => {
+ if let Ok(s) = get_lit_str(cx, RENAME_ALL, &m.lit) {
+ match RenameRule::from_str(&s.value()) {
+ Ok(rename_rule) => {
+ rename_all_ser_rule.set(&m.path, rename_rule);
+ rename_all_de_rule.set(&m.path, rename_rule);
+ }
+ Err(()) => cx.error_spanned_by(
+ s,
+ format!(
+ "unknown rename rule for #[serde(rename_all = {:?})]",
+ s.value(),
+ ),
+ ),
+ }
+ }
+ }
+
+ // Parse `#[serde(rename_all(serialize = "foo", deserialize = "bar"))]`
+ Meta(List(m)) if m.path == RENAME_ALL => {
+ if let Ok((ser, de)) = get_renames(cx, &m.nested) {
+ if let Some(ser) = ser {
+ match RenameRule::from_str(&ser.value()) {
+ Ok(rename_rule) => rename_all_ser_rule.set(&m.path, rename_rule),
+ Err(()) => cx.error_spanned_by(
+ ser,
+ format!(
+ "unknown rename rule for #[serde(rename_all = {:?})]",
+ ser.value(),
+ ),
+ ),
+ }
+ }
+ if let Some(de) = de {
+ match RenameRule::from_str(&de.value()) {
+ Ok(rename_rule) => rename_all_de_rule.set(&m.path, rename_rule),
+ Err(()) => cx.error_spanned_by(
+ de,
+ format!(
+ "unknown rename rule for #[serde(rename_all = {:?})]",
+ de.value(),
+ ),
+ ),
+ }
+ }
+ }
+ }
+
+ // Parse `#[serde(transparent)]`
+ Meta(Path(word)) if word == TRANSPARENT => {
+ transparent.set_true(word);
+ }
+
+ // Parse `#[serde(deny_unknown_fields)]`
+ Meta(Path(word)) if word == DENY_UNKNOWN_FIELDS => {
+ deny_unknown_fields.set_true(word);
+ }
+
+ // Parse `#[serde(default)]`
+ Meta(Path(word)) if word == DEFAULT => match &item.data {
+ syn::Data::Struct(syn::DataStruct { fields, .. }) => match fields {
+ syn::Fields::Named(_) => {
+ default.set(word, Default::Default);
+ }
+ syn::Fields::Unnamed(_) | syn::Fields::Unit => cx.error_spanned_by(
+ fields,
+ "#[serde(default)] can only be used on structs with named fields",
+ ),
+ },
+ syn::Data::Enum(syn::DataEnum { enum_token, .. }) => cx.error_spanned_by(
+ enum_token,
+ "#[serde(default)] can only be used on structs with named fields",
+ ),
+ syn::Data::Union(syn::DataUnion { union_token, .. }) => cx.error_spanned_by(
+ union_token,
+ "#[serde(default)] can only be used on structs with named fields",
+ ),
+ },
+
+ // Parse `#[serde(default = "...")]`
+ Meta(NameValue(m)) if m.path == DEFAULT => {
+ if let Ok(path) = parse_lit_into_expr_path(cx, DEFAULT, &m.lit) {
+ match &item.data {
+ syn::Data::Struct(syn::DataStruct { fields, .. }) => {
+ match fields {
+ syn::Fields::Named(_) => {
+ default.set(&m.path, Default::Path(path));
+ }
+ syn::Fields::Unnamed(_) | syn::Fields::Unit => cx
+ .error_spanned_by(
+ fields,
+ "#[serde(default = \"...\")] can only be used on structs with named fields",
+ ),
+ }
+ }
+ syn::Data::Enum(syn::DataEnum { enum_token, .. }) => cx
+ .error_spanned_by(
+ enum_token,
+ "#[serde(default = \"...\")] can only be used on structs with named fields",
+ ),
+ syn::Data::Union(syn::DataUnion {
+ union_token, ..
+ }) => cx.error_spanned_by(
+ union_token,
+ "#[serde(default = \"...\")] can only be used on structs with named fields",
+ ),
+ }
+ }
+ }
+
+ // Parse `#[serde(bound = "T: SomeBound")]`
+ Meta(NameValue(m)) if m.path == BOUND => {
+ if let Ok(where_predicates) = parse_lit_into_where(cx, BOUND, BOUND, &m.lit) {
+ ser_bound.set(&m.path, where_predicates.clone());
+ de_bound.set(&m.path, where_predicates);
+ }
+ }
+
+ // Parse `#[serde(bound(serialize = "...", deserialize = "..."))]`
+ Meta(List(m)) if m.path == BOUND => {
+ if let Ok((ser, de)) = get_where_predicates(cx, &m.nested) {
+ ser_bound.set_opt(&m.path, ser);
+ de_bound.set_opt(&m.path, de);
+ }
+ }
+
+ // Parse `#[serde(untagged)]`
+ Meta(Path(word)) if word == UNTAGGED => match item.data {
+ syn::Data::Enum(_) => {
+ untagged.set_true(word);
+ }
+ syn::Data::Struct(syn::DataStruct { struct_token, .. }) => {
+ cx.error_spanned_by(
+ struct_token,
+ "#[serde(untagged)] can only be used on enums",
+ );
+ }
+ syn::Data::Union(syn::DataUnion { union_token, .. }) => {
+ cx.error_spanned_by(
+ union_token,
+ "#[serde(untagged)] can only be used on enums",
+ );
+ }
+ },
+
+ // Parse `#[serde(tag = "type")]`
+ Meta(NameValue(m)) if m.path == TAG => {
+ if let Ok(s) = get_lit_str(cx, TAG, &m.lit) {
+ match &item.data {
+ syn::Data::Enum(_) => {
+ internal_tag.set(&m.path, s.value());
+ }
+ syn::Data::Struct(syn::DataStruct { fields, .. }) => match fields {
+ syn::Fields::Named(_) => {
+ internal_tag.set(&m.path, s.value());
+ }
+ syn::Fields::Unnamed(_) | syn::Fields::Unit => {
+ cx.error_spanned_by(
+ fields,
+ "#[serde(tag = \"...\")] can only be used on enums and structs with named fields",
+ );
+ }
+ },
+ syn::Data::Union(syn::DataUnion { union_token, .. }) => {
+ cx.error_spanned_by(
+ union_token,
+ "#[serde(tag = \"...\")] can only be used on enums and structs with named fields",
+ );
+ }
+ }
+ }
+ }
+
+ // Parse `#[serde(content = "c")]`
+ Meta(NameValue(m)) if m.path == CONTENT => {
+ if let Ok(s) = get_lit_str(cx, CONTENT, &m.lit) {
+ match &item.data {
+ syn::Data::Enum(_) => {
+ content.set(&m.path, s.value());
+ }
+ syn::Data::Struct(syn::DataStruct { struct_token, .. }) => {
+ cx.error_spanned_by(
+ struct_token,
+ "#[serde(content = \"...\")] can only be used on enums",
+ );
+ }
+ syn::Data::Union(syn::DataUnion { union_token, .. }) => {
+ cx.error_spanned_by(
+ union_token,
+ "#[serde(content = \"...\")] can only be used on enums",
+ );
+ }
+ }
+ }
+ }
+
+ // Parse `#[serde(from = "Type")]
+ Meta(NameValue(m)) if m.path == FROM => {
+ if let Ok(from_ty) = parse_lit_into_ty(cx, FROM, &m.lit) {
+ type_from.set_opt(&m.path, Some(from_ty));
+ }
+ }
+
+ // Parse `#[serde(try_from = "Type")]
+ Meta(NameValue(m)) if m.path == TRY_FROM => {
+ if let Ok(try_from_ty) = parse_lit_into_ty(cx, TRY_FROM, &m.lit) {
+ type_try_from.set_opt(&m.path, Some(try_from_ty));
+ }
+ }
+
+ // Parse `#[serde(into = "Type")]
+ Meta(NameValue(m)) if m.path == INTO => {
+ if let Ok(into_ty) = parse_lit_into_ty(cx, INTO, &m.lit) {
+ type_into.set_opt(&m.path, Some(into_ty));
+ }
+ }
+
+ // Parse `#[serde(remote = "...")]`
+ Meta(NameValue(m)) if m.path == REMOTE => {
+ if let Ok(path) = parse_lit_into_path(cx, REMOTE, &m.lit) {
+ if is_primitive_path(&path, "Self") {
+ remote.set(&m.path, item.ident.clone().into());
+ } else {
+ remote.set(&m.path, path);
+ }
+ }
+ }
+
+ // Parse `#[serde(field_identifier)]`
+ Meta(Path(word)) if word == FIELD_IDENTIFIER => {
+ field_identifier.set_true(word);
+ }
+
+ // Parse `#[serde(variant_identifier)]`
+ Meta(Path(word)) if word == VARIANT_IDENTIFIER => {
+ variant_identifier.set_true(word);
+ }
+
+ // Parse `#[serde(crate = "foo")]`
+ Meta(NameValue(m)) if m.path == CRATE => {
+ if let Ok(path) = parse_lit_into_path(cx, CRATE, &m.lit) {
+ serde_path.set(&m.path, path)
+ }
+ }
+
+ Meta(meta_item) => {
+ let path = meta_item
+ .path()
+ .into_token_stream()
+ .to_string()
+ .replace(' ', "");
+ cx.error_spanned_by(
+ meta_item.path(),
+ format!("unknown serde container attribute `{}`", path),
+ );
+ }
+
+ Lit(lit) => {
+ cx.error_spanned_by(lit, "unexpected literal in serde container attribute");
+ }
+ }
+ }
+
+ let mut is_packed = false;
+ for attr in &item.attrs {
+ if attr.path.is_ident("repr") {
+ let _ = attr.parse_args_with(|input: ParseStream| {
+ while let Some(token) = input.parse()? {
+ if let TokenTree::Ident(ident) = token {
+ is_packed |= ident == "packed";
+ }
+ }
+ Ok(())
+ });
+ }
+ }
+
+ Container {
+ name: Name::from_attrs(unraw(&item.ident), ser_name, de_name, None),
+ transparent: transparent.get(),
+ deny_unknown_fields: deny_unknown_fields.get(),
+ default: default.get().unwrap_or(Default::None),
+ rename_all_rules: RenameAllRules {
+ serialize: rename_all_ser_rule.get().unwrap_or(RenameRule::None),
+ deserialize: rename_all_de_rule.get().unwrap_or(RenameRule::None),
+ },
+ ser_bound: ser_bound.get(),
+ de_bound: de_bound.get(),
+ tag: decide_tag(cx, item, untagged, internal_tag, content),
+ type_from: type_from.get(),
+ type_try_from: type_try_from.get(),
+ 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,
+ }
+ }
+
+ pub fn name(&self) -> &Name {
+ &self.name
+ }
+
+ pub fn rename_all_rules(&self) -> &RenameAllRules {
+ &self.rename_all_rules
+ }
+
+ pub fn transparent(&self) -> bool {
+ self.transparent
+ }
+
+ pub fn deny_unknown_fields(&self) -> bool {
+ self.deny_unknown_fields
+ }
+
+ pub fn default(&self) -> &Default {
+ &self.default
+ }
+
+ pub fn ser_bound(&self) -> Option<&[syn::WherePredicate]> {
+ self.ser_bound.as_ref().map(|vec| &vec[..])
+ }
+
+ pub fn de_bound(&self) -> Option<&[syn::WherePredicate]> {
+ self.de_bound.as_ref().map(|vec| &vec[..])
+ }
+
+ pub fn tag(&self) -> &TagType {
+ &self.tag
+ }
+
+ pub fn type_from(&self) -> Option<&syn::Type> {
+ self.type_from.as_ref()
+ }
+
+ pub fn type_try_from(&self) -> Option<&syn::Type> {
+ self.type_try_from.as_ref()
+ }
+
+ pub fn type_into(&self) -> Option<&syn::Type> {
+ self.type_into.as_ref()
+ }
+
+ pub fn remote(&self) -> Option<&syn::Path> {
+ self.remote.as_ref()
+ }
+
+ pub fn is_packed(&self) -> bool {
+ self.is_packed
+ }
+
+ pub fn identifier(&self) -> Identifier {
+ 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()
+ }
+
+ pub fn serde_path(&self) -> Cow<syn::Path> {
+ self.custom_serde_path()
+ .map_or_else(|| Cow::Owned(parse_quote!(_serde)), Cow::Borrowed)
+ }
+}
+
+fn decide_tag(
+ cx: &Ctxt,
+ item: &syn::DeriveInput,
+ untagged: BoolAttr,
+ internal_tag: Attr<String>,
+ content: Attr<String>,
+) -> TagType {
+ match (
+ untagged.0.get_with_tokens(),
+ internal_tag.get_with_tokens(),
+ content.get_with_tokens(),
+ ) {
+ (None, None, None) => TagType::External,
+ (Some(_), None, None) => TagType::None,
+ (None, Some((_, tag)), None) => {
+ // Check that there are no tuple variants.
+ if let syn::Data::Enum(data) = &item.data {
+ for variant in &data.variants {
+ match &variant.fields {
+ syn::Fields::Named(_) | syn::Fields::Unit => {}
+ syn::Fields::Unnamed(fields) => {
+ if fields.unnamed.len() != 1 {
+ cx.error_spanned_by(
+ variant,
+ "#[serde(tag = \"...\")] cannot be used with tuple variants",
+ );
+ break;
+ }
+ }
+ }
+ }
+ }
+ TagType::Internal { tag }
+ }
+ (Some((untagged_tokens, _)), Some((tag_tokens, _)), None) => {
+ cx.error_spanned_by(
+ untagged_tokens,
+ "enum cannot be both untagged and internally tagged",
+ );
+ cx.error_spanned_by(
+ tag_tokens,
+ "enum cannot be both untagged and internally tagged",
+ );
+ TagType::External // doesn't matter, will error
+ }
+ (None, None, Some((content_tokens, _))) => {
+ cx.error_spanned_by(
+ content_tokens,
+ "#[serde(tag = \"...\", content = \"...\")] must be used together",
+ );
+ TagType::External
+ }
+ (Some((untagged_tokens, _)), None, Some((content_tokens, _))) => {
+ cx.error_spanned_by(
+ untagged_tokens,
+ "untagged enum cannot have #[serde(content = \"...\")]",
+ );
+ cx.error_spanned_by(
+ content_tokens,
+ "untagged enum cannot have #[serde(content = \"...\")]",
+ );
+ TagType::External
+ }
+ (None, Some((_, tag)), Some((_, content))) => TagType::Adjacent { tag, content },
+ (Some((untagged_tokens, _)), Some((tag_tokens, _)), Some((content_tokens, _))) => {
+ cx.error_spanned_by(
+ untagged_tokens,
+ "untagged enum cannot have #[serde(tag = \"...\", content = \"...\")]",
+ );
+ cx.error_spanned_by(
+ tag_tokens,
+ "untagged enum cannot have #[serde(tag = \"...\", content = \"...\")]",
+ );
+ cx.error_spanned_by(
+ content_tokens,
+ "untagged enum cannot have #[serde(tag = \"...\", content = \"...\")]",
+ );
+ TagType::External
+ }
+ }
+}
+
+fn decide_identifier(
+ cx: &Ctxt,
+ item: &syn::DeriveInput,
+ field_identifier: BoolAttr,
+ variant_identifier: BoolAttr,
+) -> Identifier {
+ match (
+ &item.data,
+ field_identifier.0.get_with_tokens(),
+ variant_identifier.0.get_with_tokens(),
+ ) {
+ (_, None, None) => Identifier::No,
+ (_, Some((field_identifier_tokens, _)), Some((variant_identifier_tokens, _))) => {
+ cx.error_spanned_by(
+ field_identifier_tokens,
+ "#[serde(field_identifier)] and #[serde(variant_identifier)] cannot both be set",
+ );
+ cx.error_spanned_by(
+ variant_identifier_tokens,
+ "#[serde(field_identifier)] and #[serde(variant_identifier)] cannot both be set",
+ );
+ Identifier::No
+ }
+ (syn::Data::Enum(_), Some(_), None) => Identifier::Field,
+ (syn::Data::Enum(_), None, Some(_)) => Identifier::Variant,
+ (syn::Data::Struct(syn::DataStruct { struct_token, .. }), Some(_), None) => {
+ cx.error_spanned_by(
+ struct_token,
+ "#[serde(field_identifier)] can only be used on an enum",
+ );
+ Identifier::No
+ }
+ (syn::Data::Union(syn::DataUnion { union_token, .. }), Some(_), None) => {
+ cx.error_spanned_by(
+ union_token,
+ "#[serde(field_identifier)] can only be used on an enum",
+ );
+ Identifier::No
+ }
+ (syn::Data::Struct(syn::DataStruct { struct_token, .. }), None, Some(_)) => {
+ cx.error_spanned_by(
+ struct_token,
+ "#[serde(variant_identifier)] can only be used on an enum",
+ );
+ Identifier::No
+ }
+ (syn::Data::Union(syn::DataUnion { union_token, .. }), None, Some(_)) => {
+ cx.error_spanned_by(
+ union_token,
+ "#[serde(variant_identifier)] can only be used on an enum",
+ );
+ Identifier::No
+ }
+ }
+}
+
+/// Represents variant attribute information
+pub struct Variant {
+ name: Name,
+ rename_all_rules: RenameAllRules,
+ ser_bound: Option<Vec<syn::WherePredicate>>,
+ de_bound: Option<Vec<syn::WherePredicate>>,
+ skip_deserializing: bool,
+ skip_serializing: bool,
+ other: bool,
+ serialize_with: Option<syn::ExprPath>,
+ deserialize_with: Option<syn::ExprPath>,
+ borrow: Option<syn::Meta>,
+}
+
+impl Variant {
+ pub fn from_ast(cx: &Ctxt, variant: &syn::Variant) -> Self {
+ let mut ser_name = Attr::none(cx, RENAME);
+ let mut de_name = Attr::none(cx, RENAME);
+ let mut de_aliases = VecAttr::none(cx, RENAME);
+ let mut skip_deserializing = BoolAttr::none(cx, SKIP_DESERIALIZING);
+ let mut skip_serializing = BoolAttr::none(cx, SKIP_SERIALIZING);
+ let mut rename_all_ser_rule = Attr::none(cx, RENAME_ALL);
+ let mut rename_all_de_rule = Attr::none(cx, RENAME_ALL);
+ let mut ser_bound = Attr::none(cx, BOUND);
+ let mut de_bound = Attr::none(cx, BOUND);
+ let mut other = BoolAttr::none(cx, OTHER);
+ let mut serialize_with = Attr::none(cx, SERIALIZE_WITH);
+ let mut deserialize_with = Attr::none(cx, DESERIALIZE_WITH);
+ let mut borrow = Attr::none(cx, BORROW);
+
+ for meta_item in variant
+ .attrs
+ .iter()
+ .flat_map(|attr| get_serde_meta_items(cx, attr))
+ .flatten()
+ {
+ match &meta_item {
+ // Parse `#[serde(rename = "foo")]`
+ Meta(NameValue(m)) if m.path == RENAME => {
+ if let Ok(s) = get_lit_str(cx, RENAME, &m.lit) {
+ ser_name.set(&m.path, s.value());
+ de_name.set_if_none(s.value());
+ de_aliases.insert(&m.path, s.value());
+ }
+ }
+
+ // Parse `#[serde(rename(serialize = "foo", deserialize = "bar"))]`
+ Meta(List(m)) if m.path == RENAME => {
+ if let Ok((ser, de)) = get_multiple_renames(cx, &m.nested) {
+ ser_name.set_opt(&m.path, ser.map(syn::LitStr::value));
+ for de_value in de {
+ de_name.set_if_none(de_value.value());
+ de_aliases.insert(&m.path, de_value.value());
+ }
+ }
+ }
+
+ // Parse `#[serde(alias = "foo")]`
+ Meta(NameValue(m)) if m.path == ALIAS => {
+ if let Ok(s) = get_lit_str(cx, ALIAS, &m.lit) {
+ de_aliases.insert(&m.path, s.value());
+ }
+ }
+
+ // Parse `#[serde(rename_all = "foo")]`
+ Meta(NameValue(m)) if m.path == RENAME_ALL => {
+ if let Ok(s) = get_lit_str(cx, RENAME_ALL, &m.lit) {
+ match RenameRule::from_str(&s.value()) {
+ Ok(rename_rule) => {
+ rename_all_ser_rule.set(&m.path, rename_rule);
+ rename_all_de_rule.set(&m.path, rename_rule);
+ }
+ Err(()) => cx.error_spanned_by(
+ s,
+ format!(
+ "unknown rename rule for #[serde(rename_all = {:?})]",
+ s.value()
+ ),
+ ),
+ }
+ }
+ }
+
+ // Parse `#[serde(rename_all(serialize = "foo", deserialize = "bar"))]`
+ Meta(List(m)) if m.path == RENAME_ALL => {
+ if let Ok((ser, de)) = get_renames(cx, &m.nested) {
+ if let Some(ser) = ser {
+ match RenameRule::from_str(&ser.value()) {
+ Ok(rename_rule) => rename_all_ser_rule.set(&m.path, rename_rule),
+ Err(()) => cx.error_spanned_by(
+ ser,
+ format!(
+ "unknown rename rule for #[serde(rename_all = {:?})]",
+ ser.value(),
+ ),
+ ),
+ }
+ }
+ if let Some(de) = de {
+ match RenameRule::from_str(&de.value()) {
+ Ok(rename_rule) => rename_all_de_rule.set(&m.path, rename_rule),
+ Err(()) => cx.error_spanned_by(
+ de,
+ format!(
+ "unknown rename rule for #[serde(rename_all = {:?})]",
+ de.value(),
+ ),
+ ),
+ }
+ }
+ }
+ }
+
+ // Parse `#[serde(skip)]`
+ Meta(Path(word)) if word == SKIP => {
+ skip_serializing.set_true(word);
+ skip_deserializing.set_true(word);
+ }
+
+ // Parse `#[serde(skip_deserializing)]`
+ Meta(Path(word)) if word == SKIP_DESERIALIZING => {
+ skip_deserializing.set_true(word);
+ }
+
+ // Parse `#[serde(skip_serializing)]`
+ Meta(Path(word)) if word == SKIP_SERIALIZING => {
+ skip_serializing.set_true(word);
+ }
+
+ // Parse `#[serde(other)]`
+ Meta(Path(word)) if word == OTHER => {
+ other.set_true(word);
+ }
+
+ // Parse `#[serde(bound = "T: SomeBound")]`
+ Meta(NameValue(m)) if m.path == BOUND => {
+ if let Ok(where_predicates) = parse_lit_into_where(cx, BOUND, BOUND, &m.lit) {
+ ser_bound.set(&m.path, where_predicates.clone());
+ de_bound.set(&m.path, where_predicates);
+ }
+ }
+
+ // Parse `#[serde(bound(serialize = "...", deserialize = "..."))]`
+ Meta(List(m)) if m.path == BOUND => {
+ if let Ok((ser, de)) = get_where_predicates(cx, &m.nested) {
+ ser_bound.set_opt(&m.path, ser);
+ de_bound.set_opt(&m.path, de);
+ }
+ }
+
+ // Parse `#[serde(with = "...")]`
+ Meta(NameValue(m)) if m.path == WITH => {
+ if let Ok(path) = parse_lit_into_expr_path(cx, WITH, &m.lit) {
+ let mut ser_path = path.clone();
+ ser_path
+ .path
+ .segments
+ .push(Ident::new("serialize", Span::call_site()).into());
+ serialize_with.set(&m.path, ser_path);
+ let mut de_path = path;
+ de_path
+ .path
+ .segments
+ .push(Ident::new("deserialize", Span::call_site()).into());
+ deserialize_with.set(&m.path, de_path);
+ }
+ }
+
+ // Parse `#[serde(serialize_with = "...")]`
+ Meta(NameValue(m)) if m.path == SERIALIZE_WITH => {
+ if let Ok(path) = parse_lit_into_expr_path(cx, SERIALIZE_WITH, &m.lit) {
+ serialize_with.set(&m.path, path);
+ }
+ }
+
+ // Parse `#[serde(deserialize_with = "...")]`
+ Meta(NameValue(m)) if m.path == DESERIALIZE_WITH => {
+ if let Ok(path) = parse_lit_into_expr_path(cx, DESERIALIZE_WITH, &m.lit) {
+ deserialize_with.set(&m.path, path);
+ }
+ }
+
+ // Defer `#[serde(borrow)]` and `#[serde(borrow = "'a + 'b")]`
+ Meta(m) if m.path() == BORROW => match &variant.fields {
+ syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
+ borrow.set(m.path(), m.clone());
+ }
+ _ => {
+ cx.error_spanned_by(
+ variant,
+ "#[serde(borrow)] may only be used on newtype variants",
+ );
+ }
+ },
+
+ Meta(meta_item) => {
+ let path = meta_item
+ .path()
+ .into_token_stream()
+ .to_string()
+ .replace(' ', "");
+ cx.error_spanned_by(
+ meta_item.path(),
+ format!("unknown serde variant attribute `{}`", path),
+ );
+ }
+
+ Lit(lit) => {
+ cx.error_spanned_by(lit, "unexpected literal in serde variant attribute");
+ }
+ }
+ }
+
+ Variant {
+ name: Name::from_attrs(unraw(&variant.ident), ser_name, de_name, Some(de_aliases)),
+ rename_all_rules: RenameAllRules {
+ serialize: rename_all_ser_rule.get().unwrap_or(RenameRule::None),
+ deserialize: rename_all_de_rule.get().unwrap_or(RenameRule::None),
+ },
+ ser_bound: ser_bound.get(),
+ de_bound: de_bound.get(),
+ skip_deserializing: skip_deserializing.get(),
+ skip_serializing: skip_serializing.get(),
+ other: other.get(),
+ serialize_with: serialize_with.get(),
+ deserialize_with: deserialize_with.get(),
+ borrow: borrow.get(),
+ }
+ }
+
+ pub fn name(&self) -> &Name {
+ &self.name
+ }
+
+ pub fn aliases(&self) -> Vec<String> {
+ self.name.deserialize_aliases()
+ }
+
+ pub fn rename_by_rules(&mut self, rules: &RenameAllRules) {
+ if !self.name.serialize_renamed {
+ self.name.serialize = rules.serialize.apply_to_variant(&self.name.serialize);
+ }
+ if !self.name.deserialize_renamed {
+ self.name.deserialize = rules.deserialize.apply_to_variant(&self.name.deserialize);
+ }
+ }
+
+ pub fn rename_all_rules(&self) -> &RenameAllRules {
+ &self.rename_all_rules
+ }
+
+ pub fn ser_bound(&self) -> Option<&[syn::WherePredicate]> {
+ self.ser_bound.as_ref().map(|vec| &vec[..])
+ }
+
+ pub fn de_bound(&self) -> Option<&[syn::WherePredicate]> {
+ self.de_bound.as_ref().map(|vec| &vec[..])
+ }
+
+ pub fn skip_deserializing(&self) -> bool {
+ self.skip_deserializing
+ }
+
+ pub fn skip_serializing(&self) -> bool {
+ self.skip_serializing
+ }
+
+ pub fn other(&self) -> bool {
+ self.other
+ }
+
+ pub fn serialize_with(&self) -> Option<&syn::ExprPath> {
+ self.serialize_with.as_ref()
+ }
+
+ pub fn deserialize_with(&self) -> Option<&syn::ExprPath> {
+ self.deserialize_with.as_ref()
+ }
+}
+
+/// Represents field attribute information
+pub struct Field {
+ name: Name,
+ skip_serializing: bool,
+ skip_deserializing: bool,
+ skip_serializing_if: Option<syn::ExprPath>,
+ default: Default,
+ serialize_with: Option<syn::ExprPath>,
+ deserialize_with: Option<syn::ExprPath>,
+ ser_bound: Option<Vec<syn::WherePredicate>>,
+ de_bound: Option<Vec<syn::WherePredicate>>,
+ borrowed_lifetimes: BTreeSet<syn::Lifetime>,
+ getter: Option<syn::ExprPath>,
+ flatten: bool,
+ transparent: bool,
+}
+
+/// Represents the default to use for a field when deserializing.
+pub enum Default {
+ /// Field must always be specified because it does not have a default.
+ None,
+ /// The default is given by `std::default::Default::default()`.
+ Default,
+ /// The default is given by this function.
+ Path(syn::ExprPath),
+}
+
+impl Default {
+ pub fn is_none(&self) -> bool {
+ match self {
+ Default::None => true,
+ Default::Default | Default::Path(_) => false,
+ }
+ }
+}
+
+impl Field {
+ /// Extract out the `#[serde(...)]` attributes from a struct field.
+ pub fn from_ast(
+ cx: &Ctxt,
+ index: usize,
+ field: &syn::Field,
+ attrs: Option<&Variant>,
+ container_default: &Default,
+ ) -> Self {
+ let mut ser_name = Attr::none(cx, RENAME);
+ let mut de_name = Attr::none(cx, RENAME);
+ let mut de_aliases = VecAttr::none(cx, RENAME);
+ let mut skip_serializing = BoolAttr::none(cx, SKIP_SERIALIZING);
+ let mut skip_deserializing = BoolAttr::none(cx, SKIP_DESERIALIZING);
+ let mut skip_serializing_if = Attr::none(cx, SKIP_SERIALIZING_IF);
+ let mut default = Attr::none(cx, DEFAULT);
+ let mut serialize_with = Attr::none(cx, SERIALIZE_WITH);
+ let mut deserialize_with = Attr::none(cx, DESERIALIZE_WITH);
+ let mut ser_bound = Attr::none(cx, BOUND);
+ let mut de_bound = Attr::none(cx, BOUND);
+ let mut borrowed_lifetimes = Attr::none(cx, BORROW);
+ let mut getter = Attr::none(cx, GETTER);
+ let mut flatten = BoolAttr::none(cx, FLATTEN);
+
+ let ident = match &field.ident {
+ Some(ident) => unraw(ident),
+ None => index.to_string(),
+ };
+
+ let variant_borrow = attrs
+ .and_then(|variant| variant.borrow.as_ref())
+ .map(|borrow| Meta(borrow.clone()));
+
+ for meta_item in field
+ .attrs
+ .iter()
+ .flat_map(|attr| get_serde_meta_items(cx, attr))
+ .flatten()
+ .chain(variant_borrow)
+ {
+ match &meta_item {
+ // Parse `#[serde(rename = "foo")]`
+ Meta(NameValue(m)) if m.path == RENAME => {
+ if let Ok(s) = get_lit_str(cx, RENAME, &m.lit) {
+ ser_name.set(&m.path, s.value());
+ de_name.set_if_none(s.value());
+ de_aliases.insert(&m.path, s.value());
+ }
+ }
+
+ // Parse `#[serde(rename(serialize = "foo", deserialize = "bar"))]`
+ Meta(List(m)) if m.path == RENAME => {
+ if let Ok((ser, de)) = get_multiple_renames(cx, &m.nested) {
+ ser_name.set_opt(&m.path, ser.map(syn::LitStr::value));
+ for de_value in de {
+ de_name.set_if_none(de_value.value());
+ de_aliases.insert(&m.path, de_value.value());
+ }
+ }
+ }
+
+ // Parse `#[serde(alias = "foo")]`
+ Meta(NameValue(m)) if m.path == ALIAS => {
+ if let Ok(s) = get_lit_str(cx, ALIAS, &m.lit) {
+ de_aliases.insert(&m.path, s.value());
+ }
+ }
+
+ // Parse `#[serde(default)]`
+ Meta(Path(word)) if word == DEFAULT => {
+ default.set(word, Default::Default);
+ }
+
+ // Parse `#[serde(default = "...")]`
+ Meta(NameValue(m)) if m.path == DEFAULT => {
+ if let Ok(path) = parse_lit_into_expr_path(cx, DEFAULT, &m.lit) {
+ default.set(&m.path, Default::Path(path));
+ }
+ }
+
+ // Parse `#[serde(skip_serializing)]`
+ Meta(Path(word)) if word == SKIP_SERIALIZING => {
+ skip_serializing.set_true(word);
+ }
+
+ // Parse `#[serde(skip_deserializing)]`
+ Meta(Path(word)) if word == SKIP_DESERIALIZING => {
+ skip_deserializing.set_true(word);
+ }
+
+ // Parse `#[serde(skip)]`
+ Meta(Path(word)) if word == SKIP => {
+ skip_serializing.set_true(word);
+ skip_deserializing.set_true(word);
+ }
+
+ // Parse `#[serde(skip_serializing_if = "...")]`
+ Meta(NameValue(m)) if m.path == SKIP_SERIALIZING_IF => {
+ if let Ok(path) = parse_lit_into_expr_path(cx, SKIP_SERIALIZING_IF, &m.lit) {
+ skip_serializing_if.set(&m.path, path);
+ }
+ }
+
+ // Parse `#[serde(serialize_with = "...")]`
+ Meta(NameValue(m)) if m.path == SERIALIZE_WITH => {
+ if let Ok(path) = parse_lit_into_expr_path(cx, SERIALIZE_WITH, &m.lit) {
+ serialize_with.set(&m.path, path);
+ }
+ }
+
+ // Parse `#[serde(deserialize_with = "...")]`
+ Meta(NameValue(m)) if m.path == DESERIALIZE_WITH => {
+ if let Ok(path) = parse_lit_into_expr_path(cx, DESERIALIZE_WITH, &m.lit) {
+ deserialize_with.set(&m.path, path);
+ }
+ }
+
+ // Parse `#[serde(with = "...")]`
+ Meta(NameValue(m)) if m.path == WITH => {
+ if let Ok(path) = parse_lit_into_expr_path(cx, WITH, &m.lit) {
+ let mut ser_path = path.clone();
+ ser_path
+ .path
+ .segments
+ .push(Ident::new("serialize", Span::call_site()).into());
+ serialize_with.set(&m.path, ser_path);
+ let mut de_path = path;
+ de_path
+ .path
+ .segments
+ .push(Ident::new("deserialize", Span::call_site()).into());
+ deserialize_with.set(&m.path, de_path);
+ }
+ }
+
+ // Parse `#[serde(bound = "T: SomeBound")]`
+ Meta(NameValue(m)) if m.path == BOUND => {
+ if let Ok(where_predicates) = parse_lit_into_where(cx, BOUND, BOUND, &m.lit) {
+ ser_bound.set(&m.path, where_predicates.clone());
+ de_bound.set(&m.path, where_predicates);
+ }
+ }
+
+ // Parse `#[serde(bound(serialize = "...", deserialize = "..."))]`
+ Meta(List(m)) if m.path == BOUND => {
+ if let Ok((ser, de)) = get_where_predicates(cx, &m.nested) {
+ ser_bound.set_opt(&m.path, ser);
+ de_bound.set_opt(&m.path, de);
+ }
+ }
+
+ // Parse `#[serde(borrow)]`
+ Meta(Path(word)) if word == BORROW => {
+ if let Ok(borrowable) = borrowable_lifetimes(cx, &ident, field) {
+ borrowed_lifetimes.set(word, borrowable);
+ }
+ }
+
+ // Parse `#[serde(borrow = "'a + 'b")]`
+ Meta(NameValue(m)) if m.path == BORROW => {
+ if let Ok(lifetimes) = parse_lit_into_lifetimes(cx, BORROW, &m.lit) {
+ if let Ok(borrowable) = borrowable_lifetimes(cx, &ident, field) {
+ for lifetime in &lifetimes {
+ if !borrowable.contains(lifetime) {
+ cx.error_spanned_by(
+ field,
+ format!(
+ "field `{}` does not have lifetime {}",
+ ident, lifetime
+ ),
+ );
+ }
+ }
+ borrowed_lifetimes.set(&m.path, lifetimes);
+ }
+ }
+ }
+
+ // Parse `#[serde(getter = "...")]`
+ Meta(NameValue(m)) if m.path == GETTER => {
+ if let Ok(path) = parse_lit_into_expr_path(cx, GETTER, &m.lit) {
+ getter.set(&m.path, path);
+ }
+ }
+
+ // Parse `#[serde(flatten)]`
+ Meta(Path(word)) if word == FLATTEN => {
+ flatten.set_true(word);
+ }
+
+ Meta(meta_item) => {
+ let path = meta_item
+ .path()
+ .into_token_stream()
+ .to_string()
+ .replace(' ', "");
+ cx.error_spanned_by(
+ meta_item.path(),
+ format!("unknown serde field attribute `{}`", path),
+ );
+ }
+
+ Lit(lit) => {
+ cx.error_spanned_by(lit, "unexpected literal in serde field attribute");
+ }
+ }
+ }
+
+ // Is skip_deserializing, initialize the field to Default::default() unless a
+ // different default is specified by `#[serde(default = "...")]` on
+ // ourselves or our container (e.g. the struct we are in).
+ if let Default::None = *container_default {
+ if skip_deserializing.0.value.is_some() {
+ default.set_if_none(Default::Default);
+ }
+ }
+
+ let mut borrowed_lifetimes = borrowed_lifetimes.get().unwrap_or_default();
+ if !borrowed_lifetimes.is_empty() {
+ // Cow<str> and Cow<[u8]> never borrow by default:
+ //
+ // impl<'de, 'a, T: ?Sized> Deserialize<'de> for Cow<'a, T>
+ //
+ // A #[serde(borrow)] attribute enables borrowing that corresponds
+ // roughly to these impls:
+ //
+ // impl<'de: 'a, 'a> Deserialize<'de> for Cow<'a, str>
+ // impl<'de: 'a, 'a> Deserialize<'de> for Cow<'a, [u8]>
+ if is_cow(&field.ty, is_str) {
+ let mut path = syn::Path {
+ leading_colon: None,
+ segments: Punctuated::new(),
+ };
+ let span = Span::call_site();
+ path.segments.push(Ident::new("_serde", span).into());
+ path.segments.push(Ident::new("private", span).into());
+ path.segments.push(Ident::new("de", span).into());
+ path.segments
+ .push(Ident::new("borrow_cow_str", span).into());
+ let expr = syn::ExprPath {
+ attrs: Vec::new(),
+ qself: None,
+ path,
+ };
+ deserialize_with.set_if_none(expr);
+ } else if is_cow(&field.ty, is_slice_u8) {
+ let mut path = syn::Path {
+ leading_colon: None,
+ segments: Punctuated::new(),
+ };
+ let span = Span::call_site();
+ path.segments.push(Ident::new("_serde", span).into());
+ path.segments.push(Ident::new("private", span).into());
+ path.segments.push(Ident::new("de", span).into());
+ path.segments
+ .push(Ident::new("borrow_cow_bytes", span).into());
+ let expr = syn::ExprPath {
+ attrs: Vec::new(),
+ qself: None,
+ path,
+ };
+ deserialize_with.set_if_none(expr);
+ }
+ } else if is_implicitly_borrowed(&field.ty) {
+ // Types &str and &[u8] are always implicitly borrowed. No need for
+ // a #[serde(borrow)].
+ collect_lifetimes(&field.ty, &mut borrowed_lifetimes);
+ }
+
+ Field {
+ name: Name::from_attrs(ident, ser_name, de_name, Some(de_aliases)),
+ skip_serializing: skip_serializing.get(),
+ skip_deserializing: skip_deserializing.get(),
+ skip_serializing_if: skip_serializing_if.get(),
+ default: default.get().unwrap_or(Default::None),
+ serialize_with: serialize_with.get(),
+ deserialize_with: deserialize_with.get(),
+ ser_bound: ser_bound.get(),
+ de_bound: de_bound.get(),
+ borrowed_lifetimes,
+ getter: getter.get(),
+ flatten: flatten.get(),
+ transparent: false,
+ }
+ }
+
+ pub fn name(&self) -> &Name {
+ &self.name
+ }
+
+ pub fn aliases(&self) -> Vec<String> {
+ self.name.deserialize_aliases()
+ }
+
+ pub fn rename_by_rules(&mut self, rules: &RenameAllRules) {
+ if !self.name.serialize_renamed {
+ self.name.serialize = rules.serialize.apply_to_field(&self.name.serialize);
+ }
+ if !self.name.deserialize_renamed {
+ self.name.deserialize = rules.deserialize.apply_to_field(&self.name.deserialize);
+ }
+ }
+
+ pub fn skip_serializing(&self) -> bool {
+ self.skip_serializing
+ }
+
+ pub fn skip_deserializing(&self) -> bool {
+ self.skip_deserializing
+ }
+
+ pub fn skip_serializing_if(&self) -> Option<&syn::ExprPath> {
+ self.skip_serializing_if.as_ref()
+ }
+
+ pub fn default(&self) -> &Default {
+ &self.default
+ }
+
+ pub fn serialize_with(&self) -> Option<&syn::ExprPath> {
+ self.serialize_with.as_ref()
+ }
+
+ pub fn deserialize_with(&self) -> Option<&syn::ExprPath> {
+ self.deserialize_with.as_ref()
+ }
+
+ pub fn ser_bound(&self) -> Option<&[syn::WherePredicate]> {
+ self.ser_bound.as_ref().map(|vec| &vec[..])
+ }
+
+ pub fn de_bound(&self) -> Option<&[syn::WherePredicate]> {
+ self.de_bound.as_ref().map(|vec| &vec[..])
+ }
+
+ pub fn borrowed_lifetimes(&self) -> &BTreeSet<syn::Lifetime> {
+ &self.borrowed_lifetimes
+ }
+
+ pub fn getter(&self) -> Option<&syn::ExprPath> {
+ self.getter.as_ref()
+ }
+
+ pub fn flatten(&self) -> bool {
+ self.flatten
+ }
+
+ pub fn transparent(&self) -> bool {
+ self.transparent
+ }
+
+ pub fn mark_transparent(&mut self) {
+ self.transparent = true;
+ }
+}
+
+type SerAndDe<T> = (Option<T>, Option<T>);
+
+fn get_ser_and_de<'a, 'b, T, F>(
+ cx: &'b Ctxt,
+ attr_name: Symbol,
+ metas: &'a Punctuated<syn::NestedMeta, Token![,]>,
+ f: F,
+) -> Result<(VecAttr<'b, T>, VecAttr<'b, T>), ()>
+where
+ T: 'a,
+ F: Fn(&Ctxt, Symbol, Symbol, &'a syn::Lit) -> Result<T, ()>,
+{
+ let mut ser_meta = VecAttr::none(cx, attr_name);
+ let mut de_meta = VecAttr::none(cx, attr_name);
+
+ for meta in metas {
+ match meta {
+ Meta(NameValue(meta)) if meta.path == SERIALIZE => {
+ if let Ok(v) = f(cx, attr_name, SERIALIZE, &meta.lit) {
+ ser_meta.insert(&meta.path, v);
+ }
+ }
+
+ Meta(NameValue(meta)) if meta.path == DESERIALIZE => {
+ if let Ok(v) = f(cx, attr_name, DESERIALIZE, &meta.lit) {
+ de_meta.insert(&meta.path, v);
+ }
+ }
+
+ _ => {
+ cx.error_spanned_by(
+ meta,
+ format!(
+ "malformed {0} attribute, expected `{0}(serialize = ..., deserialize = ...)`",
+ attr_name
+ ),
+ );
+ return Err(());
+ }
+ }
+ }
+
+ Ok((ser_meta, de_meta))
+}
+
+fn get_renames<'a>(
+ cx: &Ctxt,
+ items: &'a Punctuated<syn::NestedMeta, Token![,]>,
+) -> Result<SerAndDe<&'a syn::LitStr>, ()> {
+ let (ser, de) = get_ser_and_de(cx, RENAME, items, get_lit_str2)?;
+ Ok((ser.at_most_one()?, de.at_most_one()?))
+}
+
+fn get_multiple_renames<'a>(
+ cx: &Ctxt,
+ items: &'a Punctuated<syn::NestedMeta, Token![,]>,
+) -> Result<(Option<&'a syn::LitStr>, Vec<&'a syn::LitStr>), ()> {
+ let (ser, de) = get_ser_and_de(cx, RENAME, items, get_lit_str2)?;
+ Ok((ser.at_most_one()?, de.get()))
+}
+
+fn get_where_predicates(
+ cx: &Ctxt,
+ items: &Punctuated<syn::NestedMeta, Token![,]>,
+) -> Result<SerAndDe<Vec<syn::WherePredicate>>, ()> {
+ let (ser, de) = get_ser_and_de(cx, BOUND, items, parse_lit_into_where)?;
+ Ok((ser.at_most_one()?, de.at_most_one()?))
+}
+
+pub fn get_serde_meta_items(cx: &Ctxt, attr: &syn::Attribute) -> Result<Vec<syn::NestedMeta>, ()> {
+ if attr.path != SERDE {
+ return Ok(Vec::new());
+ }
+
+ match attr.parse_meta() {
+ Ok(List(meta)) => Ok(meta.nested.into_iter().collect()),
+ Ok(other) => {
+ cx.error_spanned_by(other, "expected #[serde(...)]");
+ Err(())
+ }
+ Err(err) => {
+ cx.syn_error(err);
+ Err(())
+ }
+ }
+}
+
+fn get_lit_str<'a>(cx: &Ctxt, attr_name: Symbol, lit: &'a syn::Lit) -> Result<&'a syn::LitStr, ()> {
+ get_lit_str2(cx, attr_name, attr_name, lit)
+}
+
+fn get_lit_str2<'a>(
+ cx: &Ctxt,
+ attr_name: Symbol,
+ meta_item_name: Symbol,
+ lit: &'a syn::Lit,
+) -> Result<&'a syn::LitStr, ()> {
+ if let syn::Lit::Str(lit) = lit {
+ Ok(lit)
+ } else {
+ cx.error_spanned_by(
+ lit,
+ format!(
+ "expected serde {} attribute to be a string: `{} = \"...\"`",
+ attr_name, meta_item_name
+ ),
+ );
+ Err(())
+ }
+}
+
+fn parse_lit_into_path(cx: &Ctxt, attr_name: Symbol, lit: &syn::Lit) -> Result<syn::Path, ()> {
+ let string = get_lit_str(cx, attr_name, lit)?;
+ parse_lit_str(string).map_err(|_| {
+ cx.error_spanned_by(lit, format!("failed to parse path: {:?}", string.value()))
+ })
+}
+
+fn parse_lit_into_expr_path(
+ cx: &Ctxt,
+ attr_name: Symbol,
+ lit: &syn::Lit,
+) -> Result<syn::ExprPath, ()> {
+ let string = get_lit_str(cx, attr_name, lit)?;
+ parse_lit_str(string).map_err(|_| {
+ cx.error_spanned_by(lit, format!("failed to parse path: {:?}", string.value()))
+ })
+}
+
+fn parse_lit_into_where(
+ cx: &Ctxt,
+ attr_name: Symbol,
+ meta_item_name: Symbol,
+ lit: &syn::Lit,
+) -> Result<Vec<syn::WherePredicate>, ()> {
+ let string = get_lit_str2(cx, attr_name, meta_item_name, lit)?;
+ if string.value().is_empty() {
+ return Ok(Vec::new());
+ }
+
+ let where_string = syn::LitStr::new(&format!("where {}", string.value()), string.span());
+
+ parse_lit_str::<syn::WhereClause>(&where_string)
+ .map(|wh| wh.predicates.into_iter().collect())
+ .map_err(|err| cx.error_spanned_by(lit, err))
+}
+
+fn parse_lit_into_ty(cx: &Ctxt, attr_name: Symbol, lit: &syn::Lit) -> Result<syn::Type, ()> {
+ let string = get_lit_str(cx, attr_name, lit)?;
+
+ parse_lit_str(string).map_err(|_| {
+ cx.error_spanned_by(
+ lit,
+ format!("failed to parse type: {} = {:?}", attr_name, string.value()),
+ )
+ })
+}
+
+// Parses a string literal like "'a + 'b + 'c" containing a nonempty list of
+// lifetimes separated by `+`.
+fn parse_lit_into_lifetimes(
+ cx: &Ctxt,
+ attr_name: Symbol,
+ lit: &syn::Lit,
+) -> Result<BTreeSet<syn::Lifetime>, ()> {
+ let string = get_lit_str(cx, attr_name, lit)?;
+ if string.value().is_empty() {
+ cx.error_spanned_by(lit, "at least one lifetime must be borrowed");
+ return Err(());
+ }
+
+ struct BorrowedLifetimes(Punctuated<syn::Lifetime, Token![+]>);
+
+ impl Parse for BorrowedLifetimes {
+ fn parse(input: ParseStream) -> parse::Result<Self> {
+ Punctuated::parse_separated_nonempty(input).map(BorrowedLifetimes)
+ }
+ }
+
+ if let Ok(BorrowedLifetimes(lifetimes)) = parse_lit_str(string) {
+ let mut set = BTreeSet::new();
+ for lifetime in lifetimes {
+ if !set.insert(lifetime.clone()) {
+ cx.error_spanned_by(lit, format!("duplicate borrowed lifetime `{}`", lifetime));
+ }
+ }
+ return Ok(set);
+ }
+
+ cx.error_spanned_by(
+ lit,
+ format!("failed to parse borrowed lifetimes: {:?}", string.value()),
+ );
+ Err(())
+}
+
+fn is_implicitly_borrowed(ty: &syn::Type) -> bool {
+ is_implicitly_borrowed_reference(ty) || is_option(ty, is_implicitly_borrowed_reference)
+}
+
+fn is_implicitly_borrowed_reference(ty: &syn::Type) -> bool {
+ is_reference(ty, is_str) || is_reference(ty, is_slice_u8)
+}
+
+// Whether the type looks like it might be `std::borrow::Cow<T>` where elem="T".
+// This can have false negatives and false positives.
+//
+// False negative:
+//
+// use std::borrow::Cow as Pig;
+//
+// #[derive(Deserialize)]
+// struct S<'a> {
+// #[serde(borrow)]
+// pig: Pig<'a, str>,
+// }
+//
+// False positive:
+//
+// type str = [i16];
+//
+// #[derive(Deserialize)]
+// struct S<'a> {
+// #[serde(borrow)]
+// cow: Cow<'a, str>,
+// }
+fn is_cow(ty: &syn::Type, elem: fn(&syn::Type) -> bool) -> bool {
+ let path = match ungroup(ty) {
+ syn::Type::Path(ty) => &ty.path,
+ _ => {
+ return false;
+ }
+ };
+ let seg = match path.segments.last() {
+ Some(seg) => seg,
+ None => {
+ return false;
+ }
+ };
+ let args = match &seg.arguments {
+ syn::PathArguments::AngleBracketed(bracketed) => &bracketed.args,
+ _ => {
+ return false;
+ }
+ };
+ seg.ident == "Cow"
+ && args.len() == 2
+ && match (&args[0], &args[1]) {
+ (syn::GenericArgument::Lifetime(_), syn::GenericArgument::Type(arg)) => elem(arg),
+ _ => false,
+ }
+}
+
+fn is_option(ty: &syn::Type, elem: fn(&syn::Type) -> bool) -> bool {
+ let path = match ungroup(ty) {
+ syn::Type::Path(ty) => &ty.path,
+ _ => {
+ return false;
+ }
+ };
+ let seg = match path.segments.last() {
+ Some(seg) => seg,
+ None => {
+ return false;
+ }
+ };
+ let args = match &seg.arguments {
+ syn::PathArguments::AngleBracketed(bracketed) => &bracketed.args,
+ _ => {
+ return false;
+ }
+ };
+ seg.ident == "Option"
+ && args.len() == 1
+ && match &args[0] {
+ syn::GenericArgument::Type(arg) => elem(arg),
+ _ => false,
+ }
+}
+
+// Whether the type looks like it might be `&T` where elem="T". This can have
+// false negatives and false positives.
+//
+// False negative:
+//
+// type Yarn = str;
+//
+// #[derive(Deserialize)]
+// struct S<'a> {
+// r: &'a Yarn,
+// }
+//
+// False positive:
+//
+// type str = [i16];
+//
+// #[derive(Deserialize)]
+// struct S<'a> {
+// r: &'a str,
+// }
+fn is_reference(ty: &syn::Type, elem: fn(&syn::Type) -> bool) -> bool {
+ match ungroup(ty) {
+ syn::Type::Reference(ty) => ty.mutability.is_none() && elem(&ty.elem),
+ _ => false,
+ }
+}
+
+fn is_str(ty: &syn::Type) -> bool {
+ is_primitive_type(ty, "str")
+}
+
+fn is_slice_u8(ty: &syn::Type) -> bool {
+ match ungroup(ty) {
+ syn::Type::Slice(ty) => is_primitive_type(&ty.elem, "u8"),
+ _ => false,
+ }
+}
+
+fn is_primitive_type(ty: &syn::Type, primitive: &str) -> bool {
+ match ungroup(ty) {
+ syn::Type::Path(ty) => ty.qself.is_none() && is_primitive_path(&ty.path, primitive),
+ _ => false,
+ }
+}
+
+fn is_primitive_path(path: &syn::Path, primitive: &str) -> bool {
+ path.leading_colon.is_none()
+ && path.segments.len() == 1
+ && path.segments[0].ident == primitive
+ && path.segments[0].arguments.is_empty()
+}
+
+// All lifetimes that this type could borrow from a Deserializer.
+//
+// For example a type `S<'a, 'b>` could borrow `'a` and `'b`. On the other hand
+// a type `for<'a> fn(&'a str)` could not borrow `'a` from the Deserializer.
+//
+// This is used when there is an explicit or implicit `#[serde(borrow)]`
+// attribute on the field so there must be at least one borrowable lifetime.
+fn borrowable_lifetimes(
+ cx: &Ctxt,
+ name: &str,
+ field: &syn::Field,
+) -> Result<BTreeSet<syn::Lifetime>, ()> {
+ let mut lifetimes = BTreeSet::new();
+ collect_lifetimes(&field.ty, &mut lifetimes);
+ if lifetimes.is_empty() {
+ cx.error_spanned_by(
+ field,
+ format!("field `{}` has no lifetimes to borrow", name),
+ );
+ Err(())
+ } else {
+ Ok(lifetimes)
+ }
+}
+
+fn collect_lifetimes(ty: &syn::Type, out: &mut BTreeSet<syn::Lifetime>) {
+ match ty {
+ syn::Type::Slice(ty) => {
+ collect_lifetimes(&ty.elem, out);
+ }
+ syn::Type::Array(ty) => {
+ collect_lifetimes(&ty.elem, out);
+ }
+ syn::Type::Ptr(ty) => {
+ collect_lifetimes(&ty.elem, out);
+ }
+ syn::Type::Reference(ty) => {
+ out.extend(ty.lifetime.iter().cloned());
+ collect_lifetimes(&ty.elem, out);
+ }
+ syn::Type::Tuple(ty) => {
+ for elem in &ty.elems {
+ collect_lifetimes(elem, out);
+ }
+ }
+ syn::Type::Path(ty) => {
+ if let Some(qself) = &ty.qself {
+ collect_lifetimes(&qself.ty, out);
+ }
+ for seg in &ty.path.segments {
+ if let syn::PathArguments::AngleBracketed(bracketed) = &seg.arguments {
+ for arg in &bracketed.args {
+ match arg {
+ syn::GenericArgument::Lifetime(lifetime) => {
+ out.insert(lifetime.clone());
+ }
+ syn::GenericArgument::Type(ty) => {
+ collect_lifetimes(ty, out);
+ }
+ syn::GenericArgument::Binding(binding) => {
+ collect_lifetimes(&binding.ty, out);
+ }
+ syn::GenericArgument::Constraint(_)
+ | syn::GenericArgument::Const(_) => {}
+ }
+ }
+ }
+ }
+ }
+ syn::Type::Paren(ty) => {
+ collect_lifetimes(&ty.elem, out);
+ }
+ syn::Type::Group(ty) => {
+ collect_lifetimes(&ty.elem, out);
+ }
+ syn::Type::BareFn(_)
+ | syn::Type::Never(_)
+ | syn::Type::TraitObject(_)
+ | syn::Type::ImplTrait(_)
+ | syn::Type::Infer(_)
+ | syn::Type::Macro(_)
+ | syn::Type::Verbatim(_)
+ | _ => {}
+ }
+}
+
+fn parse_lit_str<T>(s: &syn::LitStr) -> parse::Result<T>
+where
+ T: Parse,
+{
+ let tokens = spanned_tokens(s)?;
+ syn::parse2(tokens)
+}
+
+fn spanned_tokens(s: &syn::LitStr) -> parse::Result<TokenStream> {
+ let stream = syn::parse_str(&s.value())?;
+ Ok(respan_token_stream(stream, s.span()))
+}
+
+fn respan_token_stream(stream: TokenStream, span: Span) -> TokenStream {
+ stream
+ .into_iter()
+ .map(|token| respan_token_tree(token, span))
+ .collect()
+}
+
+fn respan_token_tree(mut token: TokenTree, span: Span) -> TokenTree {
+ if let TokenTree::Group(g) = &mut token {
+ *g = Group::new(g.delimiter(), respan_token_stream(g.stream(), span));
+ }
+ token.set_span(span);
+ token
+}
diff --git a/src/internals/case.rs b/src/internals/case.rs
new file mode 100644
index 0000000..3fcbb32
--- /dev/null
+++ b/src/internals/case.rs
@@ -0,0 +1,174 @@
+//! Code to convert the Rust-styled field/variant (e.g. `my_field`, `MyType`) to the
+//! case of the source (e.g. `my-field`, `MY_FIELD`).
+
+// See https://users.rust-lang.org/t/psa-dealing-with-warning-unused-import-std-ascii-asciiext-in-today-s-nightly/13726
+#[allow(deprecated, unused_imports)]
+use std::ascii::AsciiExt;
+
+use std::str::FromStr;
+
+use self::RenameRule::*;
+
+/// The different possible ways to change case of fields in a struct, or variants in an enum.
+#[derive(Copy, Clone, PartialEq)]
+pub enum RenameRule {
+ /// Don't apply a default rename rule.
+ None,
+ /// Rename direct children to "lowercase" style.
+ LowerCase,
+ /// Rename direct children to "UPPERCASE" style.
+ UPPERCASE,
+ /// Rename direct children to "PascalCase" style, as typically used for
+ /// enum variants.
+ PascalCase,
+ /// Rename direct children to "camelCase" style.
+ CamelCase,
+ /// Rename direct children to "snake_case" style, as commonly used for
+ /// fields.
+ SnakeCase,
+ /// Rename direct children to "SCREAMING_SNAKE_CASE" style, as commonly
+ /// used for constants.
+ ScreamingSnakeCase,
+ /// Rename direct children to "kebab-case" style.
+ KebabCase,
+ /// Rename direct children to "SCREAMING-KEBAB-CASE" style.
+ ScreamingKebabCase,
+}
+
+impl RenameRule {
+ /// Apply a renaming rule to an enum variant, returning the version expected in the source.
+ pub fn apply_to_variant(&self, variant: &str) -> String {
+ match *self {
+ None | PascalCase => variant.to_owned(),
+ LowerCase => variant.to_ascii_lowercase(),
+ UPPERCASE => variant.to_ascii_uppercase(),
+ CamelCase => variant[..1].to_ascii_lowercase() + &variant[1..],
+ SnakeCase => {
+ let mut snake = String::new();
+ for (i, ch) in variant.char_indices() {
+ if i > 0 && ch.is_uppercase() {
+ snake.push('_');
+ }
+ snake.push(ch.to_ascii_lowercase());
+ }
+ snake
+ }
+ ScreamingSnakeCase => SnakeCase.apply_to_variant(variant).to_ascii_uppercase(),
+ KebabCase => SnakeCase.apply_to_variant(variant).replace('_', "-"),
+ ScreamingKebabCase => ScreamingSnakeCase
+ .apply_to_variant(variant)
+ .replace('_', "-"),
+ }
+ }
+
+ /// Apply a renaming rule to a struct field, returning the version expected in the source.
+ pub fn apply_to_field(&self, field: &str) -> String {
+ match *self {
+ None | LowerCase | SnakeCase => field.to_owned(),
+ UPPERCASE => field.to_ascii_uppercase(),
+ PascalCase => {
+ let mut pascal = String::new();
+ let mut capitalize = true;
+ for ch in field.chars() {
+ if ch == '_' {
+ capitalize = true;
+ } else if capitalize {
+ pascal.push(ch.to_ascii_uppercase());
+ capitalize = false;
+ } else {
+ pascal.push(ch);
+ }
+ }
+ pascal
+ }
+ CamelCase => {
+ let pascal = PascalCase.apply_to_field(field);
+ pascal[..1].to_ascii_lowercase() + &pascal[1..]
+ }
+ ScreamingSnakeCase => field.to_ascii_uppercase(),
+ KebabCase => field.replace('_', "-"),
+ ScreamingKebabCase => ScreamingSnakeCase.apply_to_field(field).replace('_', "-"),
+ }
+ }
+}
+
+impl FromStr for RenameRule {
+ type Err = ();
+
+ fn from_str(rename_all_str: &str) -> Result<Self, Self::Err> {
+ match rename_all_str {
+ "lowercase" => Ok(LowerCase),
+ "UPPERCASE" => Ok(UPPERCASE),
+ "PascalCase" => Ok(PascalCase),
+ "camelCase" => Ok(CamelCase),
+ "snake_case" => Ok(SnakeCase),
+ "SCREAMING_SNAKE_CASE" => Ok(ScreamingSnakeCase),
+ "kebab-case" => Ok(KebabCase),
+ "SCREAMING-KEBAB-CASE" => Ok(ScreamingKebabCase),
+ _ => Err(()),
+ }
+ }
+}
+
+#[test]
+fn rename_variants() {
+ for &(original, lower, upper, camel, snake, screaming, kebab, screaming_kebab) in &[
+ (
+ "Outcome", "outcome", "OUTCOME", "outcome", "outcome", "OUTCOME", "outcome", "OUTCOME",
+ ),
+ (
+ "VeryTasty",
+ "verytasty",
+ "VERYTASTY",
+ "veryTasty",
+ "very_tasty",
+ "VERY_TASTY",
+ "very-tasty",
+ "VERY-TASTY",
+ ),
+ ("A", "a", "A", "a", "a", "A", "a", "A"),
+ ("Z42", "z42", "Z42", "z42", "z42", "Z42", "z42", "Z42"),
+ ] {
+ assert_eq!(None.apply_to_variant(original), original);
+ assert_eq!(LowerCase.apply_to_variant(original), lower);
+ assert_eq!(UPPERCASE.apply_to_variant(original), upper);
+ assert_eq!(PascalCase.apply_to_variant(original), original);
+ assert_eq!(CamelCase.apply_to_variant(original), camel);
+ assert_eq!(SnakeCase.apply_to_variant(original), snake);
+ assert_eq!(ScreamingSnakeCase.apply_to_variant(original), screaming);
+ assert_eq!(KebabCase.apply_to_variant(original), kebab);
+ assert_eq!(
+ ScreamingKebabCase.apply_to_variant(original),
+ screaming_kebab
+ );
+ }
+}
+
+#[test]
+fn rename_fields() {
+ for &(original, upper, pascal, camel, screaming, kebab, screaming_kebab) in &[
+ (
+ "outcome", "OUTCOME", "Outcome", "outcome", "OUTCOME", "outcome", "OUTCOME",
+ ),
+ (
+ "very_tasty",
+ "VERY_TASTY",
+ "VeryTasty",
+ "veryTasty",
+ "VERY_TASTY",
+ "very-tasty",
+ "VERY-TASTY",
+ ),
+ ("a", "A", "A", "a", "A", "a", "A"),
+ ("z42", "Z42", "Z42", "z42", "Z42", "z42", "Z42"),
+ ] {
+ assert_eq!(None.apply_to_field(original), original);
+ assert_eq!(UPPERCASE.apply_to_field(original), upper);
+ assert_eq!(PascalCase.apply_to_field(original), pascal);
+ assert_eq!(CamelCase.apply_to_field(original), camel);
+ assert_eq!(SnakeCase.apply_to_field(original), original);
+ assert_eq!(ScreamingSnakeCase.apply_to_field(original), screaming);
+ assert_eq!(KebabCase.apply_to_field(original), kebab);
+ assert_eq!(ScreamingKebabCase.apply_to_field(original), screaming_kebab);
+ }
+}
diff --git a/src/internals/check.rs b/src/internals/check.rs
new file mode 100644
index 0000000..30ede1c
--- /dev/null
+++ b/src/internals/check.rs
@@ -0,0 +1,420 @@
+use internals::ast::{Container, Data, Field, Style};
+use internals::attr::{Identifier, TagType};
+use internals::{ungroup, Ctxt, Derive};
+use syn::{Member, Type};
+
+/// Cross-cutting checks that require looking at more than a single attrs
+/// object. Simpler checks should happen when parsing and building the attrs.
+pub fn check(cx: &Ctxt, cont: &mut Container, derive: Derive) {
+ check_getter(cx, cont);
+ check_flatten(cx, cont);
+ check_identifier(cx, cont);
+ check_variant_skip_attrs(cx, cont);
+ check_internal_tag_field_name_conflict(cx, cont);
+ check_adjacent_tag_conflict(cx, cont);
+ check_transparent(cx, cont, derive);
+ check_from_and_try_from(cx, cont);
+}
+
+/// Getters are only allowed inside structs (not enums) with the `remote`
+/// attribute.
+fn check_getter(cx: &Ctxt, cont: &Container) {
+ match cont.data {
+ Data::Enum(_) => {
+ if cont.data.has_getter() {
+ cx.error_spanned_by(
+ cont.original,
+ "#[serde(getter = \"...\")] is not allowed in an enum",
+ );
+ }
+ }
+ Data::Struct(_, _) => {
+ if cont.data.has_getter() && cont.attrs.remote().is_none() {
+ cx.error_spanned_by(
+ cont.original,
+ "#[serde(getter = \"...\")] can only be used in structs that have #[serde(remote = \"...\")]",
+ );
+ }
+ }
+ }
+}
+
+/// Flattening has some restrictions we can test.
+fn check_flatten(cx: &Ctxt, cont: &Container) {
+ match &cont.data {
+ Data::Enum(variants) => {
+ for variant in variants {
+ for field in &variant.fields {
+ check_flatten_field(cx, variant.style, field);
+ }
+ }
+ }
+ Data::Struct(style, fields) => {
+ for field in fields {
+ check_flatten_field(cx, *style, field);
+ }
+ }
+ }
+}
+
+fn check_flatten_field(cx: &Ctxt, style: Style, field: &Field) {
+ if !field.attrs.flatten() {
+ return;
+ }
+ match style {
+ Style::Tuple => {
+ cx.error_spanned_by(
+ field.original,
+ "#[serde(flatten)] cannot be used on tuple structs",
+ );
+ }
+ Style::Newtype => {
+ cx.error_spanned_by(
+ field.original,
+ "#[serde(flatten)] cannot be used on newtype structs",
+ );
+ }
+ _ => {}
+ }
+}
+
+/// The `other` attribute must be used at most once and it must be the last
+/// variant of an enum.
+///
+/// Inside a `variant_identifier` all variants must be unit variants. Inside a
+/// `field_identifier` all but possibly one variant must be unit variants. The
+/// last variant may be a newtype variant which is an implicit "other" case.
+fn check_identifier(cx: &Ctxt, cont: &Container) {
+ let variants = match &cont.data {
+ Data::Enum(variants) => variants,
+ Data::Struct(_, _) => {
+ return;
+ }
+ };
+
+ for (i, variant) in variants.iter().enumerate() {
+ match (
+ variant.style,
+ cont.attrs.identifier(),
+ variant.attrs.other(),
+ cont.attrs.tag(),
+ ) {
+ // The `other` attribute may not be used in a variant_identifier.
+ (_, Identifier::Variant, true, _) => {
+ cx.error_spanned_by(
+ variant.original,
+ "#[serde(other)] may not be used on a variant identifier",
+ );
+ }
+
+ // Variant with `other` attribute cannot appear in untagged enum
+ (_, Identifier::No, true, &TagType::None) => {
+ cx.error_spanned_by(
+ variant.original,
+ "#[serde(other)] cannot appear on untagged enum",
+ );
+ }
+
+ // Variant with `other` attribute must be the last one.
+ (Style::Unit, Identifier::Field, true, _) | (Style::Unit, Identifier::No, true, _) => {
+ if i < variants.len() - 1 {
+ cx.error_spanned_by(
+ variant.original,
+ "#[serde(other)] must be on the last variant",
+ );
+ }
+ }
+
+ // Variant with `other` attribute must be a unit variant.
+ (_, Identifier::Field, true, _) | (_, Identifier::No, true, _) => {
+ cx.error_spanned_by(
+ variant.original,
+ "#[serde(other)] must be on a unit variant",
+ );
+ }
+
+ // Any sort of variant is allowed if this is not an identifier.
+ (_, Identifier::No, false, _) => {}
+
+ // Unit variant without `other` attribute is always fine.
+ (Style::Unit, _, false, _) => {}
+
+ // The last field is allowed to be a newtype catch-all.
+ (Style::Newtype, Identifier::Field, false, _) => {
+ if i < variants.len() - 1 {
+ cx.error_spanned_by(
+ variant.original,
+ format!("`{}` must be the last variant", variant.ident),
+ );
+ }
+ }
+
+ (_, Identifier::Field, false, _) => {
+ cx.error_spanned_by(
+ variant.original,
+ "#[serde(field_identifier)] may only contain unit variants",
+ );
+ }
+
+ (_, Identifier::Variant, false, _) => {
+ cx.error_spanned_by(
+ variant.original,
+ "#[serde(variant_identifier)] may only contain unit variants",
+ );
+ }
+ }
+ }
+}
+
+/// Skip-(de)serializing attributes are not allowed on variants marked
+/// (de)serialize_with.
+fn check_variant_skip_attrs(cx: &Ctxt, cont: &Container) {
+ let variants = match &cont.data {
+ Data::Enum(variants) => variants,
+ Data::Struct(_, _) => {
+ return;
+ }
+ };
+
+ for variant in variants.iter() {
+ if variant.attrs.serialize_with().is_some() {
+ if variant.attrs.skip_serializing() {
+ cx.error_spanned_by(
+ variant.original,
+ format!(
+ "variant `{}` cannot have both #[serde(serialize_with)] and #[serde(skip_serializing)]",
+ variant.ident
+ ),
+ );
+ }
+
+ for field in &variant.fields {
+ let member = member_message(&field.member);
+
+ if field.attrs.skip_serializing() {
+ cx.error_spanned_by(
+ variant.original,
+ format!(
+ "variant `{}` cannot have both #[serde(serialize_with)] and a field {} marked with #[serde(skip_serializing)]",
+ variant.ident, member
+ ),
+ );
+ }
+
+ if field.attrs.skip_serializing_if().is_some() {
+ cx.error_spanned_by(
+ variant.original,
+ format!(
+ "variant `{}` cannot have both #[serde(serialize_with)] and a field {} marked with #[serde(skip_serializing_if)]",
+ variant.ident, member
+ ),
+ );
+ }
+ }
+ }
+
+ if variant.attrs.deserialize_with().is_some() {
+ if variant.attrs.skip_deserializing() {
+ cx.error_spanned_by(
+ variant.original,
+ format!(
+ "variant `{}` cannot have both #[serde(deserialize_with)] and #[serde(skip_deserializing)]",
+ variant.ident
+ ),
+ );
+ }
+
+ for field in &variant.fields {
+ if field.attrs.skip_deserializing() {
+ let member = member_message(&field.member);
+
+ cx.error_spanned_by(
+ variant.original,
+ format!(
+ "variant `{}` cannot have both #[serde(deserialize_with)] and a field {} marked with #[serde(skip_deserializing)]",
+ variant.ident, member
+ ),
+ );
+ }
+ }
+ }
+ }
+}
+
+/// The tag of an internally-tagged struct variant must not be
+/// the same as either one of its fields, as this would result in
+/// duplicate keys in the serialized output and/or ambiguity in
+/// the to-be-deserialized input.
+fn check_internal_tag_field_name_conflict(cx: &Ctxt, cont: &Container) {
+ let variants = match &cont.data {
+ Data::Enum(variants) => variants,
+ Data::Struct(_, _) => return,
+ };
+
+ let tag = match cont.attrs.tag() {
+ TagType::Internal { tag } => tag.as_str(),
+ TagType::External | TagType::Adjacent { .. } | TagType::None => return,
+ };
+
+ let diagnose_conflict = || {
+ cx.error_spanned_by(
+ cont.original,
+ format!("variant field name `{}` conflicts with internal tag", tag),
+ )
+ };
+
+ for variant in variants {
+ match variant.style {
+ Style::Struct => {
+ for field in &variant.fields {
+ let check_ser = !field.attrs.skip_serializing();
+ let check_de = !field.attrs.skip_deserializing();
+ let name = field.attrs.name();
+ let ser_name = name.serialize_name();
+
+ if check_ser && ser_name == tag {
+ diagnose_conflict();
+ return;
+ }
+
+ for de_name in field.attrs.aliases() {
+ if check_de && de_name == tag {
+ diagnose_conflict();
+ return;
+ }
+ }
+ }
+ }
+ Style::Unit | Style::Newtype | Style::Tuple => {}
+ }
+ }
+}
+
+/// In the case of adjacently-tagged enums, the type and the
+/// contents tag must differ, for the same reason.
+fn check_adjacent_tag_conflict(cx: &Ctxt, cont: &Container) {
+ let (type_tag, content_tag) = match cont.attrs.tag() {
+ TagType::Adjacent { tag, content } => (tag, content),
+ TagType::Internal { .. } | TagType::External | TagType::None => return,
+ };
+
+ if type_tag == content_tag {
+ cx.error_spanned_by(
+ cont.original,
+ format!(
+ "enum tags `{}` for type and content conflict with each other",
+ type_tag
+ ),
+ );
+ }
+}
+
+/// Enums and unit structs cannot be transparent.
+fn check_transparent(cx: &Ctxt, cont: &mut Container, derive: Derive) {
+ if !cont.attrs.transparent() {
+ return;
+ }
+
+ if cont.attrs.type_from().is_some() {
+ cx.error_spanned_by(
+ cont.original,
+ "#[serde(transparent)] is not allowed with #[serde(from = \"...\")]",
+ );
+ }
+
+ if cont.attrs.type_try_from().is_some() {
+ cx.error_spanned_by(
+ cont.original,
+ "#[serde(transparent)] is not allowed with #[serde(try_from = \"...\")]",
+ );
+ }
+
+ if cont.attrs.type_into().is_some() {
+ cx.error_spanned_by(
+ cont.original,
+ "#[serde(transparent)] is not allowed with #[serde(into = \"...\")]",
+ );
+ }
+
+ let fields = match &mut cont.data {
+ Data::Enum(_) => {
+ cx.error_spanned_by(
+ cont.original,
+ "#[serde(transparent)] is not allowed on an enum",
+ );
+ return;
+ }
+ Data::Struct(Style::Unit, _) => {
+ cx.error_spanned_by(
+ cont.original,
+ "#[serde(transparent)] is not allowed on a unit struct",
+ );
+ return;
+ }
+ Data::Struct(_, fields) => fields,
+ };
+
+ let mut transparent_field = None;
+
+ for field in fields {
+ if allow_transparent(field, derive) {
+ if transparent_field.is_some() {
+ cx.error_spanned_by(
+ cont.original,
+ "#[serde(transparent)] requires struct to have at most one transparent field",
+ );
+ return;
+ }
+ transparent_field = Some(field);
+ }
+ }
+
+ match transparent_field {
+ Some(transparent_field) => transparent_field.attrs.mark_transparent(),
+ None => match derive {
+ Derive::Serialize => {
+ cx.error_spanned_by(
+ cont.original,
+ "#[serde(transparent)] requires at least one field that is not skipped",
+ );
+ }
+ Derive::Deserialize => {
+ cx.error_spanned_by(
+ cont.original,
+ "#[serde(transparent)] requires at least one field that is neither skipped nor has a default",
+ );
+ }
+ },
+ }
+}
+
+fn member_message(member: &Member) -> String {
+ match member {
+ Member::Named(ident) => format!("`{}`", ident),
+ Member::Unnamed(i) => format!("#{}", i.index),
+ }
+}
+
+fn allow_transparent(field: &Field, derive: Derive) -> bool {
+ if let Type::Path(ty) = ungroup(&field.ty) {
+ if let Some(seg) = ty.path.segments.last() {
+ if seg.ident == "PhantomData" {
+ return false;
+ }
+ }
+ }
+
+ match derive {
+ Derive::Serialize => !field.attrs.skip_serializing(),
+ Derive::Deserialize => !field.attrs.skip_deserializing() && field.attrs.default().is_none(),
+ }
+}
+
+fn check_from_and_try_from(cx: &Ctxt, cont: &mut Container) {
+ if cont.attrs.type_from().is_some() && cont.attrs.type_try_from().is_some() {
+ cx.error_spanned_by(
+ cont.original,
+ "#[serde(from = \"...\")] and #[serde(try_from = \"...\")] conflict with each other",
+ );
+ }
+}
diff --git a/src/internals/ctxt.rs b/src/internals/ctxt.rs
new file mode 100644
index 0000000..d692c2a
--- /dev/null
+++ b/src/internals/ctxt.rs
@@ -0,0 +1,62 @@
+use quote::ToTokens;
+use std::cell::RefCell;
+use std::fmt::Display;
+use std::thread;
+use syn;
+
+/// A type to collect errors together and format them.
+///
+/// Dropping this object will cause a panic. It must be consumed using `check`.
+///
+/// References can be shared since this type uses run-time exclusive mut checking.
+#[derive(Default)]
+pub struct Ctxt {
+ // The contents will be set to `None` during checking. This is so that checking can be
+ // enforced.
+ errors: RefCell<Option<Vec<syn::Error>>>,
+}
+
+impl Ctxt {
+ /// Create a new context object.
+ ///
+ /// This object contains no errors, but will still trigger a panic if it is not `check`ed.
+ pub fn new() -> Self {
+ Ctxt {
+ errors: RefCell::new(Some(Vec::new())),
+ }
+ }
+
+ /// Add an error to the context object with a tokenenizable object.
+ ///
+ /// The object is used for spanning in error messages.
+ pub fn error_spanned_by<A: ToTokens, T: Display>(&self, obj: A, msg: T) {
+ self.errors
+ .borrow_mut()
+ .as_mut()
+ .unwrap()
+ // Curb monomorphization from generating too many identical methods.
+ .push(syn::Error::new_spanned(obj.into_token_stream(), msg));
+ }
+
+ /// Add one of Syn's parse errors.
+ pub fn syn_error(&self, err: syn::Error) {
+ self.errors.borrow_mut().as_mut().unwrap().push(err);
+ }
+
+ /// Consume this object, producing a formatted error string if there are errors.
+ pub fn check(self) -> Result<(), Vec<syn::Error>> {
+ let errors = self.errors.borrow_mut().take().unwrap();
+ match errors.len() {
+ 0 => Ok(()),
+ _ => Err(errors),
+ }
+ }
+}
+
+impl Drop for Ctxt {
+ fn drop(&mut self) {
+ if !thread::panicking() && self.errors.borrow().is_some() {
+ panic!("forgot to check for errors");
+ }
+ }
+}
diff --git a/src/internals/mod.rs b/src/internals/mod.rs
new file mode 100644
index 0000000..d36b6e4
--- /dev/null
+++ b/src/internals/mod.rs
@@ -0,0 +1,24 @@
+pub mod ast;
+pub mod attr;
+
+mod ctxt;
+pub use self::ctxt::Ctxt;
+
+mod case;
+mod check;
+mod symbol;
+
+use syn::Type;
+
+#[derive(Copy, Clone)]
+pub enum Derive {
+ Serialize,
+ Deserialize,
+}
+
+pub fn ungroup(mut ty: &Type) -> &Type {
+ while let Type::Group(group) = ty {
+ ty = &group.elem;
+ }
+ ty
+}
diff --git a/src/internals/symbol.rs b/src/internals/symbol.rs
new file mode 100644
index 0000000..318b81b
--- /dev/null
+++ b/src/internals/symbol.rs
@@ -0,0 +1,67 @@
+use std::fmt::{self, Display};
+use syn::{Ident, Path};
+
+#[derive(Copy, Clone)]
+pub struct Symbol(&'static str);
+
+pub const ALIAS: Symbol = Symbol("alias");
+pub const BORROW: Symbol = Symbol("borrow");
+pub const BOUND: Symbol = Symbol("bound");
+pub const CONTENT: Symbol = Symbol("content");
+pub const CRATE: Symbol = Symbol("crate");
+pub const DEFAULT: Symbol = Symbol("default");
+pub const DENY_UNKNOWN_FIELDS: Symbol = Symbol("deny_unknown_fields");
+pub const DESERIALIZE: Symbol = Symbol("deserialize");
+pub const DESERIALIZE_WITH: Symbol = Symbol("deserialize_with");
+pub const FIELD_IDENTIFIER: Symbol = Symbol("field_identifier");
+pub const FLATTEN: Symbol = Symbol("flatten");
+pub const FROM: Symbol = Symbol("from");
+pub const GETTER: Symbol = Symbol("getter");
+pub const INTO: Symbol = Symbol("into");
+pub const OTHER: Symbol = Symbol("other");
+pub const REMOTE: Symbol = Symbol("remote");
+pub const RENAME: Symbol = Symbol("rename");
+pub const RENAME_ALL: Symbol = Symbol("rename_all");
+pub const SERDE: Symbol = Symbol("serde");
+pub const SERIALIZE: Symbol = Symbol("serialize");
+pub const SERIALIZE_WITH: Symbol = Symbol("serialize_with");
+pub const SKIP: Symbol = Symbol("skip");
+pub const SKIP_DESERIALIZING: Symbol = Symbol("skip_deserializing");
+pub const SKIP_SERIALIZING: Symbol = Symbol("skip_serializing");
+pub const SKIP_SERIALIZING_IF: Symbol = Symbol("skip_serializing_if");
+pub const TAG: Symbol = Symbol("tag");
+pub const TRANSPARENT: Symbol = Symbol("transparent");
+pub const TRY_FROM: Symbol = Symbol("try_from");
+pub const UNTAGGED: Symbol = Symbol("untagged");
+pub const VARIANT_IDENTIFIER: Symbol = Symbol("variant_identifier");
+pub const WITH: Symbol = Symbol("with");
+
+impl PartialEq<Symbol> for Ident {
+ fn eq(&self, word: &Symbol) -> bool {
+ self == word.0
+ }
+}
+
+impl<'a> PartialEq<Symbol> for &'a Ident {
+ fn eq(&self, word: &Symbol) -> bool {
+ *self == word.0
+ }
+}
+
+impl PartialEq<Symbol> for Path {
+ fn eq(&self, word: &Symbol) -> bool {
+ self.is_ident(word.0)
+ }
+}
+
+impl<'a> PartialEq<Symbol> for &'a Path {
+ fn eq(&self, word: &Symbol) -> bool {
+ self.is_ident(word.0)
+ }
+}
+
+impl Display for Symbol {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ formatter.write_str(self.0)
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..1711340
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,98 @@
+//! This crate provides Serde's two derive macros.
+//!
+//! ```edition2018
+//! # use serde_derive::{Serialize, Deserialize};
+//! #
+//! #[derive(Serialize, Deserialize)]
+//! # struct S;
+//! #
+//! # fn main() {}
+//! ```
+//!
+//! Please refer to [https://serde.rs/derive.html] for how to set this up.
+//!
+//! [https://serde.rs/derive.html]: https://serde.rs/derive.html
+
+#![doc(html_root_url = "https://docs.rs/serde_derive/1.0.117")]
+#![allow(unknown_lints, bare_trait_objects)]
+#![deny(clippy::all, clippy::pedantic)]
+// Ignored clippy lints
+#![allow(
+ clippy::cognitive_complexity,
+ clippy::enum_variant_names,
+ clippy::match_like_matches_macro,
+ clippy::needless_pass_by_value,
+ clippy::too_many_arguments,
+ clippy::trivially_copy_pass_by_ref,
+ clippy::used_underscore_binding,
+ clippy::wildcard_in_or_patterns,
+ // clippy bug: https://github.com/rust-lang/rust-clippy/issues/5704
+ clippy::unnested_or_patterns,
+)]
+// Ignored clippy_pedantic lints
+#![allow(
+ clippy::cast_possible_truncation,
+ clippy::checked_conversions,
+ clippy::doc_markdown,
+ clippy::enum_glob_use,
+ clippy::filter_map,
+ clippy::indexing_slicing,
+ clippy::items_after_statements,
+ clippy::map_err_ignore,
+ clippy::match_same_arms,
+ clippy::module_name_repetitions,
+ clippy::must_use_candidate,
+ clippy::option_if_let_else,
+ clippy::similar_names,
+ clippy::single_match_else,
+ clippy::struct_excessive_bools,
+ clippy::too_many_lines,
+ clippy::unseparated_literal_suffix,
+ clippy::use_self,
+ clippy::wildcard_imports
+)]
+
+#[macro_use]
+extern crate quote;
+#[macro_use]
+extern crate syn;
+
+extern crate proc_macro;
+extern crate proc_macro2;
+
+mod internals;
+
+use proc_macro::TokenStream;
+use syn::DeriveInput;
+
+#[macro_use]
+mod bound;
+#[macro_use]
+mod fragment;
+
+mod de;
+mod dummy;
+mod pretend;
+mod ser;
+mod try;
+
+#[proc_macro_derive(Serialize, attributes(serde))]
+pub fn derive_serialize(input: TokenStream) -> TokenStream {
+ let input = parse_macro_input!(input as DeriveInput);
+ ser::expand_derive_serialize(&input)
+ .unwrap_or_else(to_compile_errors)
+ .into()
+}
+
+#[proc_macro_derive(Deserialize, attributes(serde))]
+pub fn derive_deserialize(input: TokenStream) -> TokenStream {
+ let input = parse_macro_input!(input as DeriveInput);
+ de::expand_derive_deserialize(&input)
+ .unwrap_or_else(to_compile_errors)
+ .into()
+}
+
+fn to_compile_errors(errors: Vec<syn::Error>) -> proc_macro2::TokenStream {
+ let compile_errors = errors.iter().map(syn::Error::to_compile_error);
+ quote!(#(#compile_errors)*)
+}
diff --git a/src/pretend.rs b/src/pretend.rs
new file mode 100644
index 0000000..7233c23
--- /dev/null
+++ b/src/pretend.rs
@@ -0,0 +1,140 @@
+use proc_macro2::{Span, TokenStream};
+use syn::Ident;
+
+use internals::ast::{Container, Data, Field, Style};
+
+// Suppress dead_code warnings that would otherwise appear when using a remote
+// derive. Other than this pretend code, a struct annotated with remote derive
+// never has its fields referenced and an enum annotated with remote derive
+// never has its variants constructed.
+//
+// warning: field is never used: `i`
+// --> src/main.rs:4:20
+// |
+// 4 | struct StructDef { i: i32 }
+// | ^^^^^^
+//
+// warning: variant is never constructed: `V`
+// --> src/main.rs:8:16
+// |
+// 8 | enum EnumDef { V }
+// | ^
+//
+pub fn pretend_used(cont: &Container) -> TokenStream {
+ let pretend_fields = pretend_fields_used(cont);
+ let pretend_variants = pretend_variants_used(cont);
+
+ quote! {
+ #pretend_fields
+ #pretend_variants
+ }
+}
+
+// For structs with named fields, expands to:
+//
+// match None::<T> {
+// Some(T { a: ref __v0, b: ref __v1 }) => {}
+// _ => {}
+// }
+//
+// For enums, expands to the following but only including struct variants:
+//
+// match None::<T> {
+// Some(T::A { a: ref __v0 }) => {}
+// Some(T::B { b: ref __v0 }) => {}
+// _ => {}
+// }
+//
+// The `ref` is important in case the user has written a Drop impl on their
+// type. Rust does not allow destructuring a struct or enum that has a Drop
+// impl.
+fn pretend_fields_used(cont: &Container) -> TokenStream {
+ let type_ident = &cont.ident;
+ let (_, ty_generics, _) = cont.generics.split_for_impl();
+
+ let patterns = match &cont.data {
+ Data::Enum(variants) => variants
+ .iter()
+ .filter_map(|variant| match variant.style {
+ Style::Struct => {
+ let variant_ident = &variant.ident;
+ let pat = struct_pattern(&variant.fields);
+ Some(quote!(#type_ident::#variant_ident #pat))
+ }
+ _ => None,
+ })
+ .collect::<Vec<_>>(),
+ Data::Struct(Style::Struct, fields) => {
+ let pat = struct_pattern(fields);
+ vec![quote!(#type_ident #pat)]
+ }
+ Data::Struct(_, _) => {
+ return quote!();
+ }
+ };
+
+ quote! {
+ match _serde::export::None::<#type_ident #ty_generics> {
+ #(
+ _serde::export::Some(#patterns) => {}
+ )*
+ _ => {}
+ }
+ }
+}
+
+// Expands to one of these per enum variant:
+//
+// match None {
+// Some((__v0, __v1,)) => {
+// let _ = E::V { a: __v0, b: __v1 };
+// }
+// _ => {}
+// }
+//
+fn pretend_variants_used(cont: &Container) -> TokenStream {
+ let variants = match &cont.data {
+ Data::Enum(variants) => variants,
+ Data::Struct(_, _) => {
+ return quote!();
+ }
+ };
+
+ let type_ident = &cont.ident;
+ let (_, ty_generics, _) = cont.generics.split_for_impl();
+ let turbofish = ty_generics.as_turbofish();
+
+ let cases = variants.iter().map(|variant| {
+ let variant_ident = &variant.ident;
+ let placeholders = &(0..variant.fields.len())
+ .map(|i| Ident::new(&format!("__v{}", i), Span::call_site()))
+ .collect::<Vec<_>>();
+
+ let pat = match variant.style {
+ Style::Struct => {
+ let members = variant.fields.iter().map(|field| &field.member);
+ quote!({ #(#members: #placeholders),* })
+ }
+ Style::Tuple | Style::Newtype => quote!(( #(#placeholders),* )),
+ Style::Unit => quote!(),
+ };
+
+ quote! {
+ match _serde::export::None {
+ _serde::export::Some((#(#placeholders,)*)) => {
+ let _ = #type_ident::#variant_ident #turbofish #pat;
+ }
+ _ => {}
+ }
+ }
+ });
+
+ quote!(#(#cases)*)
+}
+
+fn struct_pattern(fields: &[Field]) -> TokenStream {
+ let members = fields.iter().map(|field| &field.member);
+ let placeholders =
+ (0..fields.len()).map(|i| Ident::new(&format!("__v{}", i), Span::call_site()));
+ quote!({ #(#members: ref #placeholders),* })
+}
diff --git a/src/ser.rs b/src/ser.rs
new file mode 100644
index 0000000..f29d738
--- /dev/null
+++ b/src/ser.rs
@@ -0,0 +1,1334 @@
+use proc_macro2::{Span, TokenStream};
+use syn::spanned::Spanned;
+use syn::{self, Ident, Index, Member};
+
+use bound;
+use dummy;
+use fragment::{Fragment, Match, Stmts};
+use internals::ast::{Container, Data, Field, Style, Variant};
+use internals::{attr, Ctxt, Derive};
+use pretend;
+
+pub fn expand_derive_serialize(input: &syn::DeriveInput) -> Result<TokenStream, Vec<syn::Error>> {
+ let ctxt = Ctxt::new();
+ let cont = match Container::from_ast(&ctxt, input, Derive::Serialize) {
+ Some(cont) => cont,
+ None => return Err(ctxt.check().unwrap_err()),
+ };
+ precondition(&ctxt, &cont);
+ ctxt.check()?;
+
+ let ident = &cont.ident;
+ let params = Parameters::new(&cont);
+ let (impl_generics, ty_generics, where_clause) = params.generics.split_for_impl();
+ let body = Stmts(serialize_body(&cont, &params));
+ let serde = cont.attrs.serde_path();
+
+ let impl_block = if let Some(remote) = cont.attrs.remote() {
+ let vis = &input.vis;
+ let used = pretend::pretend_used(&cont);
+ quote! {
+ impl #impl_generics #ident #ty_generics #where_clause {
+ #vis fn serialize<__S>(__self: &#remote #ty_generics, __serializer: __S) -> #serde::export::Result<__S::Ok, __S::Error>
+ where
+ __S: #serde::Serializer,
+ {
+ #used
+ #body
+ }
+ }
+ }
+ } else {
+ quote! {
+ #[automatically_derived]
+ impl #impl_generics #serde::Serialize for #ident #ty_generics #where_clause {
+ fn serialize<__S>(&self, __serializer: __S) -> #serde::export::Result<__S::Ok, __S::Error>
+ where
+ __S: #serde::Serializer,
+ {
+ #body
+ }
+ }
+ }
+ };
+
+ Ok(dummy::wrap_in_const(
+ cont.attrs.custom_serde_path(),
+ "SERIALIZE",
+ ident,
+ impl_block,
+ ))
+}
+
+fn precondition(cx: &Ctxt, cont: &Container) {
+ match cont.attrs.identifier() {
+ attr::Identifier::No => {}
+ attr::Identifier::Field => {
+ cx.error_spanned_by(cont.original, "field identifiers cannot be serialized");
+ }
+ attr::Identifier::Variant => {
+ cx.error_spanned_by(cont.original, "variant identifiers cannot be serialized");
+ }
+ }
+}
+
+struct Parameters {
+ /// Variable holding the value being serialized. Either `self` for local
+ /// types or `__self` for remote types.
+ self_var: Ident,
+
+ /// Path to the type the impl is for. Either a single `Ident` for local
+ /// types or `some::remote::Ident` for remote types. Does not include
+ /// generic parameters.
+ this: syn::Path,
+
+ /// Generics including any explicit and inferred bounds for the impl.
+ generics: syn::Generics,
+
+ /// Type has a `serde(remote = "...")` attribute.
+ is_remote: bool,
+
+ /// Type has a repr(packed) attribute.
+ is_packed: bool,
+}
+
+impl Parameters {
+ fn new(cont: &Container) -> Self {
+ let is_remote = cont.attrs.remote().is_some();
+ let self_var = if is_remote {
+ Ident::new("__self", Span::call_site())
+ } else {
+ Ident::new("self", Span::call_site())
+ };
+
+ let this = match cont.attrs.remote() {
+ Some(remote) => remote.clone(),
+ None => cont.ident.clone().into(),
+ };
+
+ let is_packed = cont.attrs.is_packed();
+
+ let generics = build_generics(cont);
+
+ Parameters {
+ self_var,
+ this,
+ generics,
+ is_remote,
+ is_packed,
+ }
+ }
+
+ /// Type name to use in error messages and `&'static str` arguments to
+ /// various Serializer methods.
+ fn type_name(&self) -> String {
+ self.this.segments.last().unwrap().ident.to_string()
+ }
+}
+
+// All the generics in the input, plus a bound `T: Serialize` for each generic
+// field type that will be serialized by us.
+fn build_generics(cont: &Container) -> syn::Generics {
+ let generics = bound::without_defaults(cont.generics);
+
+ let generics =
+ bound::with_where_predicates_from_fields(cont, &generics, attr::Field::ser_bound);
+
+ let generics =
+ bound::with_where_predicates_from_variants(cont, &generics, attr::Variant::ser_bound);
+
+ match cont.attrs.ser_bound() {
+ Some(predicates) => bound::with_where_predicates(&generics, predicates),
+ None => bound::with_bound(
+ cont,
+ &generics,
+ needs_serialize_bound,
+ &parse_quote!(_serde::Serialize),
+ ),
+ }
+}
+
+// Fields with a `skip_serializing` or `serialize_with` attribute, or which
+// belong to a variant with a 'skip_serializing` or `serialize_with` attribute,
+// are not serialized by us so we do not generate a bound. Fields with a `bound`
+// attribute specify their own bound so we do not generate one. All other fields
+// may need a `T: Serialize` bound where T is the type of the field.
+fn needs_serialize_bound(field: &attr::Field, variant: Option<&attr::Variant>) -> bool {
+ !field.skip_serializing()
+ && field.serialize_with().is_none()
+ && field.ser_bound().is_none()
+ && variant.map_or(true, |variant| {
+ !variant.skip_serializing()
+ && variant.serialize_with().is_none()
+ && variant.ser_bound().is_none()
+ })
+}
+
+fn serialize_body(cont: &Container, params: &Parameters) -> Fragment {
+ if cont.attrs.transparent() {
+ serialize_transparent(cont, params)
+ } else if let Some(type_into) = cont.attrs.type_into() {
+ serialize_into(params, type_into)
+ } else {
+ match &cont.data {
+ Data::Enum(variants) => serialize_enum(params, variants, &cont.attrs),
+ Data::Struct(Style::Struct, fields) => serialize_struct(params, fields, &cont.attrs),
+ Data::Struct(Style::Tuple, fields) => {
+ serialize_tuple_struct(params, fields, &cont.attrs)
+ }
+ Data::Struct(Style::Newtype, fields) => {
+ serialize_newtype_struct(params, &fields[0], &cont.attrs)
+ }
+ Data::Struct(Style::Unit, _) => serialize_unit_struct(&cont.attrs),
+ }
+ }
+}
+
+fn serialize_transparent(cont: &Container, params: &Parameters) -> Fragment {
+ let fields = match &cont.data {
+ Data::Struct(_, fields) => fields,
+ Data::Enum(_) => unreachable!(),
+ };
+
+ let self_var = &params.self_var;
+ let transparent_field = fields.iter().find(|f| f.attrs.transparent()).unwrap();
+ let member = &transparent_field.member;
+
+ let path = match transparent_field.attrs.serialize_with() {
+ Some(path) => quote!(#path),
+ None => {
+ let span = transparent_field.original.span();
+ quote_spanned!(span=> _serde::Serialize::serialize)
+ }
+ };
+
+ quote_block! {
+ #path(&#self_var.#member, __serializer)
+ }
+}
+
+fn serialize_into(params: &Parameters, type_into: &syn::Type) -> Fragment {
+ let self_var = &params.self_var;
+ quote_block! {
+ _serde::Serialize::serialize(
+ &_serde::export::Into::<#type_into>::into(_serde::export::Clone::clone(#self_var)),
+ __serializer)
+ }
+}
+
+fn serialize_unit_struct(cattrs: &attr::Container) -> Fragment {
+ let type_name = cattrs.name().serialize_name();
+
+ quote_expr! {
+ _serde::Serializer::serialize_unit_struct(__serializer, #type_name)
+ }
+}
+
+fn serialize_newtype_struct(
+ params: &Parameters,
+ field: &Field,
+ cattrs: &attr::Container,
+) -> Fragment {
+ let type_name = cattrs.name().serialize_name();
+
+ let mut field_expr = get_member(
+ params,
+ field,
+ &Member::Unnamed(Index {
+ index: 0,
+ span: Span::call_site(),
+ }),
+ );
+ if let Some(path) = field.attrs.serialize_with() {
+ field_expr = wrap_serialize_field_with(params, field.ty, path, &field_expr);
+ }
+
+ let span = field.original.span();
+ let func = quote_spanned!(span=> _serde::Serializer::serialize_newtype_struct);
+ quote_expr! {
+ #func(__serializer, #type_name, #field_expr)
+ }
+}
+
+fn serialize_tuple_struct(
+ params: &Parameters,
+ fields: &[Field],
+ cattrs: &attr::Container,
+) -> Fragment {
+ let serialize_stmts =
+ serialize_tuple_struct_visitor(fields, params, false, &TupleTrait::SerializeTupleStruct);
+
+ let type_name = cattrs.name().serialize_name();
+
+ let mut serialized_fields = fields
+ .iter()
+ .enumerate()
+ .filter(|(_, field)| !field.attrs.skip_serializing())
+ .peekable();
+
+ let let_mut = mut_if(serialized_fields.peek().is_some());
+
+ let len = serialized_fields
+ .map(|(i, field)| match field.attrs.skip_serializing_if() {
+ None => quote!(1),
+ Some(path) => {
+ let index = syn::Index {
+ index: i as u32,
+ span: Span::call_site(),
+ };
+ let field_expr = get_member(params, field, &Member::Unnamed(index));
+ quote!(if #path(#field_expr) { 0 } else { 1 })
+ }
+ })
+ .fold(quote!(0), |sum, expr| quote!(#sum + #expr));
+
+ quote_block! {
+ let #let_mut __serde_state = try!(_serde::Serializer::serialize_tuple_struct(__serializer, #type_name, #len));
+ #(#serialize_stmts)*
+ _serde::ser::SerializeTupleStruct::end(__serde_state)
+ }
+}
+
+fn serialize_struct(params: &Parameters, fields: &[Field], cattrs: &attr::Container) -> Fragment {
+ assert!(fields.len() as u64 <= u64::from(u32::max_value()));
+
+ if cattrs.has_flatten() {
+ serialize_struct_as_map(params, fields, cattrs)
+ } else {
+ serialize_struct_as_struct(params, fields, cattrs)
+ }
+}
+
+fn serialize_struct_tag_field(cattrs: &attr::Container, struct_trait: &StructTrait) -> TokenStream {
+ match cattrs.tag() {
+ attr::TagType::Internal { tag } => {
+ let type_name = cattrs.name().serialize_name();
+ let func = struct_trait.serialize_field(Span::call_site());
+ quote! {
+ try!(#func(&mut __serde_state, #tag, #type_name));
+ }
+ }
+ _ => quote! {},
+ }
+}
+
+fn serialize_struct_as_struct(
+ params: &Parameters,
+ fields: &[Field],
+ cattrs: &attr::Container,
+) -> Fragment {
+ let serialize_fields =
+ serialize_struct_visitor(fields, params, false, &StructTrait::SerializeStruct);
+
+ let type_name = cattrs.name().serialize_name();
+
+ let tag_field = serialize_struct_tag_field(cattrs, &StructTrait::SerializeStruct);
+ let tag_field_exists = !tag_field.is_empty();
+
+ let mut serialized_fields = fields
+ .iter()
+ .filter(|&field| !field.attrs.skip_serializing())
+ .peekable();
+
+ let let_mut = mut_if(serialized_fields.peek().is_some() || tag_field_exists);
+
+ 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_block! {
+ let #let_mut __serde_state = try!(_serde::Serializer::serialize_struct(__serializer, #type_name, #len));
+ #tag_field
+ #(#serialize_fields)*
+ _serde::ser::SerializeStruct::end(__serde_state)
+ }
+}
+
+fn serialize_struct_as_map(
+ params: &Parameters,
+ fields: &[Field],
+ cattrs: &attr::Container,
+) -> Fragment {
+ let serialize_fields =
+ serialize_struct_visitor(fields, params, false, &StructTrait::SerializeMap);
+
+ let tag_field = serialize_struct_tag_field(cattrs, &StructTrait::SerializeMap);
+ let tag_field_exists = !tag_field.is_empty();
+
+ let mut serialized_fields = fields
+ .iter()
+ .filter(|&field| !field.attrs.skip_serializing())
+ .peekable();
+
+ let let_mut = mut_if(serialized_fields.peek().is_some() || tag_field_exists);
+
+ let len = if cattrs.has_flatten() {
+ quote!(_serde::export::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::export::Some(#len))
+ };
+
+ quote_block! {
+ let #let_mut __serde_state = try!(_serde::Serializer::serialize_map(__serializer, #len));
+ #tag_field
+ #(#serialize_fields)*
+ _serde::ser::SerializeMap::end(__serde_state)
+ }
+}
+
+fn serialize_enum(params: &Parameters, variants: &[Variant], cattrs: &attr::Container) -> Fragment {
+ assert!(variants.len() as u64 <= u64::from(u32::max_value()));
+
+ let self_var = &params.self_var;
+
+ let arms: Vec<_> = variants
+ .iter()
+ .enumerate()
+ .map(|(variant_index, variant)| {
+ serialize_variant(params, variant, variant_index as u32, cattrs)
+ })
+ .collect();
+
+ quote_expr! {
+ match *#self_var {
+ #(#arms)*
+ }
+ }
+}
+
+fn serialize_variant(
+ params: &Parameters,
+ variant: &Variant,
+ variant_index: u32,
+ cattrs: &attr::Container,
+) -> TokenStream {
+ let this = &params.this;
+ let variant_ident = &variant.ident;
+
+ if variant.attrs.skip_serializing() {
+ let skipped_msg = format!(
+ "the enum variant {}::{} cannot be serialized",
+ params.type_name(),
+ variant_ident
+ );
+ let skipped_err = quote! {
+ _serde::export::Err(_serde::ser::Error::custom(#skipped_msg))
+ };
+ let fields_pat = match variant.style {
+ Style::Unit => quote!(),
+ Style::Newtype | Style::Tuple => quote!((..)),
+ Style::Struct => quote!({ .. }),
+ };
+ quote! {
+ #this::#variant_ident #fields_pat => #skipped_err,
+ }
+ } else {
+ // variant wasn't skipped
+ let case = match variant.style {
+ Style::Unit => {
+ quote! {
+ #this::#variant_ident
+ }
+ }
+ Style::Newtype => {
+ quote! {
+ #this::#variant_ident(ref __field0)
+ }
+ }
+ Style::Tuple => {
+ let field_names = (0..variant.fields.len())
+ .map(|i| Ident::new(&format!("__field{}", i), Span::call_site()));
+ quote! {
+ #this::#variant_ident(#(ref #field_names),*)
+ }
+ }
+ Style::Struct => {
+ let members = variant.fields.iter().map(|f| &f.member);
+ quote! {
+ #this::#variant_ident { #(ref #members),* }
+ }
+ }
+ };
+
+ let body = Match(match cattrs.tag() {
+ attr::TagType::External => {
+ serialize_externally_tagged_variant(params, variant, variant_index, cattrs)
+ }
+ attr::TagType::Internal { tag } => {
+ serialize_internally_tagged_variant(params, variant, cattrs, tag)
+ }
+ attr::TagType::Adjacent { tag, content } => {
+ serialize_adjacently_tagged_variant(params, variant, cattrs, tag, content)
+ }
+ attr::TagType::None => serialize_untagged_variant(params, variant, cattrs),
+ });
+
+ quote! {
+ #case => #body
+ }
+ }
+}
+
+fn serialize_externally_tagged_variant(
+ params: &Parameters,
+ variant: &Variant,
+ variant_index: u32,
+ cattrs: &attr::Container,
+) -> Fragment {
+ let type_name = cattrs.name().serialize_name();
+ let variant_name = variant.attrs.name().serialize_name();
+
+ if let Some(path) = variant.attrs.serialize_with() {
+ let ser = wrap_serialize_variant_with(params, path, variant);
+ return quote_expr! {
+ _serde::Serializer::serialize_newtype_variant(
+ __serializer,
+ #type_name,
+ #variant_index,
+ #variant_name,
+ #ser,
+ )
+ };
+ }
+
+ match effective_style(variant) {
+ Style::Unit => {
+ quote_expr! {
+ _serde::Serializer::serialize_unit_variant(
+ __serializer,
+ #type_name,
+ #variant_index,
+ #variant_name,
+ )
+ }
+ }
+ Style::Newtype => {
+ let field = &variant.fields[0];
+ let mut field_expr = quote!(__field0);
+ if let Some(path) = field.attrs.serialize_with() {
+ field_expr = wrap_serialize_field_with(params, field.ty, path, &field_expr);
+ }
+
+ let span = field.original.span();
+ let func = quote_spanned!(span=> _serde::Serializer::serialize_newtype_variant);
+ quote_expr! {
+ #func(
+ __serializer,
+ #type_name,
+ #variant_index,
+ #variant_name,
+ #field_expr,
+ )
+ }
+ }
+ Style::Tuple => serialize_tuple_variant(
+ TupleVariant::ExternallyTagged {
+ type_name,
+ variant_index,
+ variant_name,
+ },
+ params,
+ &variant.fields,
+ ),
+ Style::Struct => serialize_struct_variant(
+ StructVariant::ExternallyTagged {
+ variant_index,
+ variant_name,
+ },
+ params,
+ &variant.fields,
+ &type_name,
+ ),
+ }
+}
+
+fn serialize_internally_tagged_variant(
+ params: &Parameters,
+ variant: &Variant,
+ cattrs: &attr::Container,
+ tag: &str,
+) -> Fragment {
+ let type_name = cattrs.name().serialize_name();
+ let variant_name = variant.attrs.name().serialize_name();
+
+ let enum_ident_str = params.type_name();
+ let variant_ident_str = variant.ident.to_string();
+
+ if let Some(path) = variant.attrs.serialize_with() {
+ let ser = wrap_serialize_variant_with(params, path, variant);
+ return quote_expr! {
+ _serde::private::ser::serialize_tagged_newtype(
+ __serializer,
+ #enum_ident_str,
+ #variant_ident_str,
+ #tag,
+ #variant_name,
+ #ser,
+ )
+ };
+ }
+
+ match effective_style(variant) {
+ Style::Unit => {
+ quote_block! {
+ let mut __struct = try!(_serde::Serializer::serialize_struct(
+ __serializer, #type_name, 1));
+ try!(_serde::ser::SerializeStruct::serialize_field(
+ &mut __struct, #tag, #variant_name));
+ _serde::ser::SerializeStruct::end(__struct)
+ }
+ }
+ Style::Newtype => {
+ let field = &variant.fields[0];
+ let mut field_expr = quote!(__field0);
+ if let Some(path) = field.attrs.serialize_with() {
+ field_expr = wrap_serialize_field_with(params, field.ty, path, &field_expr);
+ }
+
+ let span = field.original.span();
+ let func = quote_spanned!(span=> _serde::private::ser::serialize_tagged_newtype);
+ quote_expr! {
+ #func(
+ __serializer,
+ #enum_ident_str,
+ #variant_ident_str,
+ #tag,
+ #variant_name,
+ #field_expr,
+ )
+ }
+ }
+ Style::Struct => serialize_struct_variant(
+ StructVariant::InternallyTagged { tag, variant_name },
+ params,
+ &variant.fields,
+ &type_name,
+ ),
+ Style::Tuple => unreachable!("checked in serde_derive_internals"),
+ }
+}
+
+fn serialize_adjacently_tagged_variant(
+ params: &Parameters,
+ variant: &Variant,
+ cattrs: &attr::Container,
+ tag: &str,
+ content: &str,
+) -> Fragment {
+ let this = &params.this;
+ let type_name = cattrs.name().serialize_name();
+ let variant_name = variant.attrs.name().serialize_name();
+
+ let inner = Stmts(if let Some(path) = variant.attrs.serialize_with() {
+ let ser = wrap_serialize_variant_with(params, path, variant);
+ quote_expr! {
+ _serde::Serialize::serialize(#ser, __serializer)
+ }
+ } else {
+ match effective_style(variant) {
+ Style::Unit => {
+ return quote_block! {
+ let mut __struct = try!(_serde::Serializer::serialize_struct(
+ __serializer, #type_name, 1));
+ try!(_serde::ser::SerializeStruct::serialize_field(
+ &mut __struct, #tag, #variant_name));
+ _serde::ser::SerializeStruct::end(__struct)
+ };
+ }
+ Style::Newtype => {
+ let field = &variant.fields[0];
+ let mut field_expr = quote!(__field0);
+ if let Some(path) = field.attrs.serialize_with() {
+ field_expr = wrap_serialize_field_with(params, field.ty, path, &field_expr);
+ }
+
+ let span = field.original.span();
+ let func = quote_spanned!(span=> _serde::ser::SerializeStruct::serialize_field);
+ return quote_block! {
+ let mut __struct = try!(_serde::Serializer::serialize_struct(
+ __serializer, #type_name, 2));
+ try!(_serde::ser::SerializeStruct::serialize_field(
+ &mut __struct, #tag, #variant_name));
+ try!(#func(
+ &mut __struct, #content, #field_expr));
+ _serde::ser::SerializeStruct::end(__struct)
+ };
+ }
+ Style::Tuple => {
+ serialize_tuple_variant(TupleVariant::Untagged, params, &variant.fields)
+ }
+ Style::Struct => serialize_struct_variant(
+ StructVariant::Untagged,
+ params,
+ &variant.fields,
+ &variant_name,
+ ),
+ }
+ });
+
+ let fields_ty = variant.fields.iter().map(|f| &f.ty);
+ let fields_ident: &Vec<_> = &match variant.style {
+ Style::Unit => {
+ if variant.attrs.serialize_with().is_some() {
+ vec![]
+ } else {
+ unreachable!()
+ }
+ }
+ Style::Newtype => vec![Member::Named(Ident::new("__field0", Span::call_site()))],
+ Style::Tuple => (0..variant.fields.len())
+ .map(|i| Member::Named(Ident::new(&format!("__field{}", i), Span::call_site())))
+ .collect(),
+ Style::Struct => variant.fields.iter().map(|f| f.member.clone()).collect(),
+ };
+
+ let (_, ty_generics, where_clause) = params.generics.split_for_impl();
+
+ let wrapper_generics = if fields_ident.is_empty() {
+ params.generics.clone()
+ } else {
+ bound::with_lifetime_bound(&params.generics, "'__a")
+ };
+ let (wrapper_impl_generics, wrapper_ty_generics, _) = wrapper_generics.split_for_impl();
+
+ quote_block! {
+ struct __AdjacentlyTagged #wrapper_generics #where_clause {
+ data: (#(&'__a #fields_ty,)*),
+ phantom: _serde::export::PhantomData<#this #ty_generics>,
+ }
+
+ impl #wrapper_impl_generics _serde::Serialize for __AdjacentlyTagged #wrapper_ty_generics #where_clause {
+ fn serialize<__S>(&self, __serializer: __S) -> _serde::export::Result<__S::Ok, __S::Error>
+ where
+ __S: _serde::Serializer,
+ {
+ // Elements that have skip_serializing will be unused.
+ #[allow(unused_variables)]
+ let (#(#fields_ident,)*) = self.data;
+ #inner
+ }
+ }
+
+ let mut __struct = try!(_serde::Serializer::serialize_struct(
+ __serializer, #type_name, 2));
+ try!(_serde::ser::SerializeStruct::serialize_field(
+ &mut __struct, #tag, #variant_name));
+ try!(_serde::ser::SerializeStruct::serialize_field(
+ &mut __struct, #content, &__AdjacentlyTagged {
+ data: (#(#fields_ident,)*),
+ phantom: _serde::export::PhantomData::<#this #ty_generics>,
+ }));
+ _serde::ser::SerializeStruct::end(__struct)
+ }
+}
+
+fn serialize_untagged_variant(
+ params: &Parameters,
+ variant: &Variant,
+ cattrs: &attr::Container,
+) -> Fragment {
+ if let Some(path) = variant.attrs.serialize_with() {
+ let ser = wrap_serialize_variant_with(params, path, variant);
+ return quote_expr! {
+ _serde::Serialize::serialize(#ser, __serializer)
+ };
+ }
+
+ match effective_style(variant) {
+ Style::Unit => {
+ quote_expr! {
+ _serde::Serializer::serialize_unit(__serializer)
+ }
+ }
+ Style::Newtype => {
+ let field = &variant.fields[0];
+ let mut field_expr = quote!(__field0);
+ if let Some(path) = field.attrs.serialize_with() {
+ field_expr = wrap_serialize_field_with(params, field.ty, path, &field_expr);
+ }
+
+ let span = field.original.span();
+ let func = quote_spanned!(span=> _serde::Serialize::serialize);
+ quote_expr! {
+ #func(#field_expr, __serializer)
+ }
+ }
+ Style::Tuple => serialize_tuple_variant(TupleVariant::Untagged, params, &variant.fields),
+ Style::Struct => {
+ let type_name = cattrs.name().serialize_name();
+ serialize_struct_variant(StructVariant::Untagged, params, &variant.fields, &type_name)
+ }
+ }
+}
+
+enum TupleVariant {
+ ExternallyTagged {
+ type_name: String,
+ variant_index: u32,
+ variant_name: String,
+ },
+ Untagged,
+}
+
+fn serialize_tuple_variant(
+ context: TupleVariant,
+ params: &Parameters,
+ fields: &[Field],
+) -> Fragment {
+ let tuple_trait = match context {
+ TupleVariant::ExternallyTagged { .. } => TupleTrait::SerializeTupleVariant,
+ TupleVariant::Untagged => TupleTrait::SerializeTuple,
+ };
+
+ let serialize_stmts = serialize_tuple_struct_visitor(fields, params, true, &tuple_trait);
+
+ let mut serialized_fields = fields
+ .iter()
+ .enumerate()
+ .filter(|(_, field)| !field.attrs.skip_serializing())
+ .peekable();
+
+ let let_mut = mut_if(serialized_fields.peek().is_some());
+
+ let len = serialized_fields
+ .map(|(i, field)| match field.attrs.skip_serializing_if() {
+ None => quote!(1),
+ Some(path) => {
+ let field_expr = Ident::new(&format!("__field{}", i), Span::call_site());
+ quote!(if #path(#field_expr) { 0 } else { 1 })
+ }
+ })
+ .fold(quote!(0), |sum, expr| quote!(#sum + #expr));
+
+ match context {
+ TupleVariant::ExternallyTagged {
+ type_name,
+ variant_index,
+ variant_name,
+ } => {
+ quote_block! {
+ let #let_mut __serde_state = try!(_serde::Serializer::serialize_tuple_variant(
+ __serializer,
+ #type_name,
+ #variant_index,
+ #variant_name,
+ #len));
+ #(#serialize_stmts)*
+ _serde::ser::SerializeTupleVariant::end(__serde_state)
+ }
+ }
+ TupleVariant::Untagged => {
+ quote_block! {
+ let #let_mut __serde_state = try!(_serde::Serializer::serialize_tuple(
+ __serializer,
+ #len));
+ #(#serialize_stmts)*
+ _serde::ser::SerializeTuple::end(__serde_state)
+ }
+ }
+ }
+}
+
+enum StructVariant<'a> {
+ ExternallyTagged {
+ variant_index: u32,
+ variant_name: String,
+ },
+ InternallyTagged {
+ tag: &'a str,
+ variant_name: String,
+ },
+ Untagged,
+}
+
+fn serialize_struct_variant<'a>(
+ context: StructVariant<'a>,
+ params: &Parameters,
+ fields: &[Field],
+ name: &str,
+) -> Fragment {
+ if fields.iter().any(|field| field.attrs.flatten()) {
+ return serialize_struct_variant_with_flatten(context, params, fields, name);
+ }
+
+ let struct_trait = match context {
+ StructVariant::ExternallyTagged { .. } => StructTrait::SerializeStructVariant,
+ StructVariant::InternallyTagged { .. } | StructVariant::Untagged => {
+ StructTrait::SerializeStruct
+ }
+ };
+
+ let serialize_fields = serialize_struct_visitor(fields, params, true, &struct_trait);
+
+ let mut serialized_fields = fields
+ .iter()
+ .filter(|&field| !field.attrs.skip_serializing())
+ .peekable();
+
+ let let_mut = mut_if(serialized_fields.peek().is_some());
+
+ let len = serialized_fields
+ .map(|field| {
+ let member = &field.member;
+
+ match field.attrs.skip_serializing_if() {
+ Some(path) => quote!(if #path(#member) { 0 } else { 1 }),
+ None => quote!(1),
+ }
+ })
+ .fold(quote!(0), |sum, expr| quote!(#sum + #expr));
+
+ match context {
+ StructVariant::ExternallyTagged {
+ variant_index,
+ variant_name,
+ } => {
+ quote_block! {
+ let #let_mut __serde_state = try!(_serde::Serializer::serialize_struct_variant(
+ __serializer,
+ #name,
+ #variant_index,
+ #variant_name,
+ #len,
+ ));
+ #(#serialize_fields)*
+ _serde::ser::SerializeStructVariant::end(__serde_state)
+ }
+ }
+ StructVariant::InternallyTagged { tag, variant_name } => {
+ quote_block! {
+ let mut __serde_state = try!(_serde::Serializer::serialize_struct(
+ __serializer,
+ #name,
+ #len + 1,
+ ));
+ try!(_serde::ser::SerializeStruct::serialize_field(
+ &mut __serde_state,
+ #tag,
+ #variant_name,
+ ));
+ #(#serialize_fields)*
+ _serde::ser::SerializeStruct::end(__serde_state)
+ }
+ }
+ StructVariant::Untagged => {
+ quote_block! {
+ let #let_mut __serde_state = try!(_serde::Serializer::serialize_struct(
+ __serializer,
+ #name,
+ #len,
+ ));
+ #(#serialize_fields)*
+ _serde::ser::SerializeStruct::end(__serde_state)
+ }
+ }
+ }
+}
+
+fn serialize_struct_variant_with_flatten<'a>(
+ context: StructVariant<'a>,
+ params: &Parameters,
+ fields: &[Field],
+ name: &str,
+) -> Fragment {
+ let struct_trait = StructTrait::SerializeMap;
+ let serialize_fields = serialize_struct_visitor(fields, params, true, &struct_trait);
+
+ let mut serialized_fields = fields
+ .iter()
+ .filter(|&field| !field.attrs.skip_serializing())
+ .peekable();
+
+ let let_mut = mut_if(serialized_fields.peek().is_some());
+
+ match context {
+ StructVariant::ExternallyTagged {
+ variant_index,
+ variant_name,
+ } => {
+ let this = &params.this;
+ let fields_ty = fields.iter().map(|f| &f.ty);
+ let members = &fields.iter().map(|f| &f.member).collect::<Vec<_>>();
+
+ let (_, ty_generics, where_clause) = params.generics.split_for_impl();
+ let wrapper_generics = bound::with_lifetime_bound(&params.generics, "'__a");
+ let (wrapper_impl_generics, wrapper_ty_generics, _) = wrapper_generics.split_for_impl();
+
+ quote_block! {
+ struct __EnumFlatten #wrapper_generics #where_clause {
+ data: (#(&'__a #fields_ty,)*),
+ phantom: _serde::export::PhantomData<#this #ty_generics>,
+ }
+
+ impl #wrapper_impl_generics _serde::Serialize for __EnumFlatten #wrapper_ty_generics #where_clause {
+ fn serialize<__S>(&self, __serializer: __S) -> _serde::export::Result<__S::Ok, __S::Error>
+ where
+ __S: _serde::Serializer,
+ {
+ let (#(#members,)*) = self.data;
+ let #let_mut __serde_state = try!(_serde::Serializer::serialize_map(
+ __serializer,
+ _serde::export::None));
+ #(#serialize_fields)*
+ _serde::ser::SerializeMap::end(__serde_state)
+ }
+ }
+
+ _serde::Serializer::serialize_newtype_variant(
+ __serializer,
+ #name,
+ #variant_index,
+ #variant_name,
+ &__EnumFlatten {
+ data: (#(#members,)*),
+ phantom: _serde::export::PhantomData::<#this #ty_generics>,
+ })
+ }
+ }
+ StructVariant::InternallyTagged { tag, variant_name } => {
+ quote_block! {
+ let #let_mut __serde_state = try!(_serde::Serializer::serialize_map(
+ __serializer,
+ _serde::export::None));
+ try!(_serde::ser::SerializeMap::serialize_entry(
+ &mut __serde_state,
+ #tag,
+ #variant_name,
+ ));
+ #(#serialize_fields)*
+ _serde::ser::SerializeMap::end(__serde_state)
+ }
+ }
+ StructVariant::Untagged => {
+ quote_block! {
+ let #let_mut __serde_state = try!(_serde::Serializer::serialize_map(
+ __serializer,
+ _serde::export::None));
+ #(#serialize_fields)*
+ _serde::ser::SerializeMap::end(__serde_state)
+ }
+ }
+ }
+}
+
+fn serialize_tuple_struct_visitor(
+ fields: &[Field],
+ params: &Parameters,
+ is_enum: bool,
+ tuple_trait: &TupleTrait,
+) -> Vec<TokenStream> {
+ fields
+ .iter()
+ .enumerate()
+ .filter(|(_, field)| !field.attrs.skip_serializing())
+ .map(|(i, field)| {
+ let mut field_expr = if is_enum {
+ let id = Ident::new(&format!("__field{}", i), Span::call_site());
+ quote!(#id)
+ } else {
+ get_member(
+ params,
+ field,
+ &Member::Unnamed(Index {
+ index: i as u32,
+ span: Span::call_site(),
+ }),
+ )
+ };
+
+ let skip = field
+ .attrs
+ .skip_serializing_if()
+ .map(|path| quote!(#path(#field_expr)));
+
+ if let Some(path) = field.attrs.serialize_with() {
+ field_expr = wrap_serialize_field_with(params, field.ty, path, &field_expr);
+ }
+
+ let span = field.original.span();
+ let func = tuple_trait.serialize_element(span);
+ let ser = quote! {
+ try!(#func(&mut __serde_state, #field_expr));
+ };
+
+ match skip {
+ None => ser,
+ Some(skip) => quote!(if !#skip { #ser }),
+ }
+ })
+ .collect()
+}
+
+fn serialize_struct_visitor(
+ fields: &[Field],
+ params: &Parameters,
+ is_enum: bool,
+ struct_trait: &StructTrait,
+) -> Vec<TokenStream> {
+ fields
+ .iter()
+ .filter(|&field| !field.attrs.skip_serializing())
+ .map(|field| {
+ let member = &field.member;
+
+ let mut field_expr = if is_enum {
+ quote!(#member)
+ } else {
+ get_member(params, field, &member)
+ };
+
+ let key_expr = field.attrs.name().serialize_name();
+
+ let skip = field
+ .attrs
+ .skip_serializing_if()
+ .map(|path| quote!(#path(#field_expr)));
+
+ if let Some(path) = field.attrs.serialize_with() {
+ field_expr = wrap_serialize_field_with(params, field.ty, path, &field_expr);
+ }
+
+ let span = field.original.span();
+ let ser = if field.attrs.flatten() {
+ let func = quote_spanned!(span=> _serde::Serialize::serialize);
+ quote! {
+ try!(#func(&#field_expr, _serde::private::ser::FlatMapSerializer(&mut __serde_state)));
+ }
+ } else {
+ let func = struct_trait.serialize_field(span);
+ quote! {
+ try!(#func(&mut __serde_state, #key_expr, #field_expr));
+ }
+ };
+
+ match skip {
+ None => ser,
+ Some(skip) => {
+ if let Some(skip_func) = struct_trait.skip_field(span) {
+ quote! {
+ if !#skip {
+ #ser
+ } else {
+ try!(#skip_func(&mut __serde_state, #key_expr));
+ }
+ }
+ } else {
+ quote! {
+ if !#skip {
+ #ser
+ }
+ }
+ }
+ }
+ }
+ })
+ .collect()
+}
+
+fn wrap_serialize_field_with(
+ params: &Parameters,
+ field_ty: &syn::Type,
+ serialize_with: &syn::ExprPath,
+ field_expr: &TokenStream,
+) -> TokenStream {
+ wrap_serialize_with(params, serialize_with, &[field_ty], &[quote!(#field_expr)])
+}
+
+fn wrap_serialize_variant_with(
+ params: &Parameters,
+ serialize_with: &syn::ExprPath,
+ variant: &Variant,
+) -> TokenStream {
+ let field_tys: Vec<_> = variant.fields.iter().map(|field| field.ty).collect();
+ let field_exprs: Vec<_> = variant
+ .fields
+ .iter()
+ .map(|field| {
+ let id = match &field.member {
+ Member::Named(ident) => ident.clone(),
+ Member::Unnamed(member) => {
+ Ident::new(&format!("__field{}", member.index), Span::call_site())
+ }
+ };
+ quote!(#id)
+ })
+ .collect();
+ wrap_serialize_with(
+ params,
+ serialize_with,
+ field_tys.as_slice(),
+ field_exprs.as_slice(),
+ )
+}
+
+fn wrap_serialize_with(
+ params: &Parameters,
+ serialize_with: &syn::ExprPath,
+ field_tys: &[&syn::Type],
+ field_exprs: &[TokenStream],
+) -> TokenStream {
+ let this = &params.this;
+ let (_, ty_generics, where_clause) = params.generics.split_for_impl();
+
+ let wrapper_generics = if field_exprs.is_empty() {
+ params.generics.clone()
+ } else {
+ bound::with_lifetime_bound(&params.generics, "'__a")
+ };
+ let (wrapper_impl_generics, wrapper_ty_generics, _) = wrapper_generics.split_for_impl();
+
+ let field_access = (0..field_exprs.len()).map(|n| {
+ Member::Unnamed(Index {
+ index: n as u32,
+ span: Span::call_site(),
+ })
+ });
+
+ quote!({
+ struct __SerializeWith #wrapper_impl_generics #where_clause {
+ values: (#(&'__a #field_tys, )*),
+ phantom: _serde::export::PhantomData<#this #ty_generics>,
+ }
+
+ impl #wrapper_impl_generics _serde::Serialize for __SerializeWith #wrapper_ty_generics #where_clause {
+ fn serialize<__S>(&self, __s: __S) -> _serde::export::Result<__S::Ok, __S::Error>
+ where
+ __S: _serde::Serializer,
+ {
+ #serialize_with(#(self.values.#field_access, )* __s)
+ }
+ }
+
+ &__SerializeWith {
+ values: (#(#field_exprs, )*),
+ phantom: _serde::export::PhantomData::<#this #ty_generics>,
+ }
+ })
+}
+
+// Serialization of an empty struct results in code like:
+//
+// let mut __serde_state = try!(serializer.serialize_struct("S", 0));
+// _serde::ser::SerializeStruct::end(__serde_state)
+//
+// where we want to omit the `mut` to avoid a warning.
+fn mut_if(is_mut: bool) -> Option<TokenStream> {
+ if is_mut {
+ Some(quote!(mut))
+ } else {
+ None
+ }
+}
+
+fn get_member(params: &Parameters, field: &Field, member: &Member) -> TokenStream {
+ let self_var = &params.self_var;
+ match (params.is_remote, field.attrs.getter()) {
+ (false, None) => {
+ if params.is_packed {
+ quote!(&{#self_var.#member})
+ } else {
+ quote!(&#self_var.#member)
+ }
+ }
+ (true, None) => {
+ let inner = if params.is_packed {
+ quote!(&{#self_var.#member})
+ } else {
+ quote!(&#self_var.#member)
+ };
+ let ty = field.ty;
+ quote!(_serde::private::ser::constrain::<#ty>(#inner))
+ }
+ (true, Some(getter)) => {
+ let ty = field.ty;
+ quote!(_serde::private::ser::constrain::<#ty>(&#getter(#self_var)))
+ }
+ (false, Some(_)) => {
+ unreachable!("getter is only allowed for remote impls");
+ }
+ }
+}
+
+fn effective_style(variant: &Variant) -> Style {
+ match variant.style {
+ Style::Newtype if variant.fields[0].attrs.skip_serializing() => Style::Unit,
+ other => other,
+ }
+}
+
+enum StructTrait {
+ SerializeMap,
+ SerializeStruct,
+ SerializeStructVariant,
+}
+
+impl StructTrait {
+ fn serialize_field(&self, span: Span) -> TokenStream {
+ match *self {
+ StructTrait::SerializeMap => {
+ quote_spanned!(span=> _serde::ser::SerializeMap::serialize_entry)
+ }
+ StructTrait::SerializeStruct => {
+ quote_spanned!(span=> _serde::ser::SerializeStruct::serialize_field)
+ }
+ StructTrait::SerializeStructVariant => {
+ quote_spanned!(span=> _serde::ser::SerializeStructVariant::serialize_field)
+ }
+ }
+ }
+
+ fn skip_field(&self, span: Span) -> Option<TokenStream> {
+ match *self {
+ StructTrait::SerializeMap => None,
+ StructTrait::SerializeStruct => {
+ Some(quote_spanned!(span=> _serde::ser::SerializeStruct::skip_field))
+ }
+ StructTrait::SerializeStructVariant => {
+ Some(quote_spanned!(span=> _serde::ser::SerializeStructVariant::skip_field))
+ }
+ }
+ }
+}
+
+enum TupleTrait {
+ SerializeTuple,
+ SerializeTupleStruct,
+ SerializeTupleVariant,
+}
+
+impl TupleTrait {
+ fn serialize_element(&self, span: Span) -> TokenStream {
+ match *self {
+ TupleTrait::SerializeTuple => {
+ quote_spanned!(span=> _serde::ser::SerializeTuple::serialize_element)
+ }
+ TupleTrait::SerializeTupleStruct => {
+ quote_spanned!(span=> _serde::ser::SerializeTupleStruct::serialize_field)
+ }
+ TupleTrait::SerializeTupleVariant => {
+ quote_spanned!(span=> _serde::ser::SerializeTupleVariant::serialize_field)
+ }
+ }
+ }
+}
diff --git a/src/try.rs b/src/try.rs
new file mode 100644
index 0000000..48829b6
--- /dev/null
+++ b/src/try.rs
@@ -0,0 +1,24 @@
+use proc_macro2::{Punct, Spacing, TokenStream};
+
+// None of our generated code requires the `From::from` error conversion
+// performed by the standard library's `try!` macro. With this simplified macro
+// we see a significant improvement in type checking and borrow checking time of
+// the generated code and a slight improvement in binary size.
+pub fn replacement() -> TokenStream {
+ // Cannot pass `$expr` to `quote!` prior to Rust 1.17.0 so interpolate it.
+ let dollar = Punct::new('$', Spacing::Alone);
+
+ quote! {
+ #[allow(unused_macros)]
+ macro_rules! try {
+ (#dollar __expr:expr) => {
+ match #dollar __expr {
+ _serde::export::Ok(__val) => __val,
+ _serde::export::Err(__err) => {
+ return _serde::export::Err(__err);
+ }
+ }
+ }
+ }
+ }
+}