diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/serde_with_macros | |
parent | Initial commit. (diff) | |
download | firefox-upstream.tar.xz firefox-upstream.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/serde_with_macros')
14 files changed, 922 insertions, 0 deletions
diff --git a/third_party/rust/serde_with_macros/.cargo-checksum.json b/third_party/rust/serde_with_macros/.cargo-checksum.json new file mode 100644 index 0000000000..ae7af88657 --- /dev/null +++ b/third_party/rust/serde_with_macros/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"CHANGELOG.md":"5c5566da5a308015cfd18afd2d9db716601fd35a8b241beeedf10bb334f5ded4","Cargo.toml":"a5f76263232aa33cd38d1316ab6eb8a764ffd101a7277941330a827ccbcd820b","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"7576269ea71f767b99297934c0b2367532690f8c4badc695edf8e04ab6a1e545","README.md":"dc4c1547049c0d11d544b1347aa67f4d947d761842c373729c2014dbb337e70b","src/lib.rs":"873adbaee30f72ee9c083c57711bbb1256d99fb6fdbe5e45c10e162861dca840","tests/compile-fail/skip-none-always.rs":"cdea0e110bc808f81c61d2ceb1394955b3af2be06261a85904e0e00a1948ff64","tests/compile-fail/skip-none-always.stderr":"cce5c9407f21b0a3eb880fb24cbb97a1e9ec22b21dcbd12963a745695175defd","tests/compile-fail/skip-none-not-struct.rs":"9212996740b302f2bd3b5298f439f8340c8cadddfb84212ddca975d5a78c4e12","tests/compile-fail/skip-none-not-struct.stderr":"0d1258fca42686eb120596c28d031935b1c22376954cebc858b0b7095d098e8e","tests/compiler-messages.rs":"06414ab54f1435839c79dcf649c577ec60c4c935a38967b1da989767c028f4e1","tests/skip_serializing_null.rs":"95c6c854d81f2b90e69455ae88f74b465919594920e34230374c9b8ceebfee77","tests/version_numbers.rs":"dccb4c715e3c4e6c08d9b779ad12122cded81becfde62fb54d2adf50171e5d83"},"package":"4070d2c9b9d258465ad1d82aabb985b84cd9a3afa94da25ece5a9938ba5f1606"}
\ No newline at end of file diff --git a/third_party/rust/serde_with_macros/CHANGELOG.md b/third_party/rust/serde_with_macros/CHANGELOG.md new file mode 100644 index 0000000000..19b09478af --- /dev/null +++ b/third_party/rust/serde_with_macros/CHANGELOG.md @@ -0,0 +1,33 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [1.1.0] + +### Changed + +* Bump minimal Rust version to 1.36.0 to support Rust Edition 2018 +* Improved CI pipeline by running `cargo audit` and `tarpaulin` in all configurations now. + +## [1.0.1] + +### Fixed + +* Features for the `syn` dependency were missing. + This was hidden due to the dev-dependencies whose features leaked into the normal build. + +## [1.0.0] + +Initial Release + +### Added + +* Add `skip_serializing_none` attribute, which adds `#[serde(skip_serializing_if = "Option::is_none")]` for each Option in a struct. + This is helpfull for APIs which have many optional fields. + The effect of can be negated by adding `serialize_always` on those fields, which should always be serialized. + Existing `skip_serializing_if` will never be modified and those fields keep their behavior. diff --git a/third_party/rust/serde_with_macros/Cargo.toml b/third_party/rust/serde_with_macros/Cargo.toml new file mode 100644 index 0000000000..ea8fd8dae5 --- /dev/null +++ b/third_party/rust/serde_with_macros/Cargo.toml @@ -0,0 +1,65 @@ +# 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_with_macros" +version = "1.1.0" +authors = ["jonasbb"] +description = "proc-macro library for serde_with" +documentation = "https://docs.rs/serde_with_macros/" +readme = "README.md" +keywords = ["serde", "utilities", "serialization", "deserialization"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/jonasbb/serde_with/tree/master/serde_with_macros" +[package.metadata.docs.rs] +all-features = true + +[lib] +proc-macro = true +[dependencies.proc-macro2] +version = "1.0.1" + +[dependencies.quote] +version = "1.0.0" + +[dependencies.syn] +version = "1.0.0" +features = ["full"] +[dev-dependencies.pretty_assertions] +version = "0.6.1" + +[dev-dependencies.rustversion] +version = "1.0.0" + +[dev-dependencies.serde] +version = "1.0.75" +features = ["derive"] + +[dev-dependencies.serde_json] +version = "1.0.25" + +[dev-dependencies.trybuild] +version = "1.0.14" + +[dev-dependencies.version-sync] +version = "0.8.1" +[badges.codecov] +branch = "master" +repository = "jonasbb/serde_with" +service = "github" + +[badges.maintenance] +status = "actively-developed" + +[badges.travis-ci] +branch = "master" +repository = "jonasbb/serde_with" diff --git a/third_party/rust/serde_with_macros/LICENSE-APACHE b/third_party/rust/serde_with_macros/LICENSE-APACHE new file mode 100644 index 0000000000..16fe87b06e --- /dev/null +++ b/third_party/rust/serde_with_macros/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/third_party/rust/serde_with_macros/LICENSE-MIT b/third_party/rust/serde_with_macros/LICENSE-MIT new file mode 100644 index 0000000000..9203baa055 --- /dev/null +++ b/third_party/rust/serde_with_macros/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2015 + +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/third_party/rust/serde_with_macros/README.md b/third_party/rust/serde_with_macros/README.md new file mode 100644 index 0000000000..ac02301fe9 --- /dev/null +++ b/third_party/rust/serde_with_macros/README.md @@ -0,0 +1,80 @@ +# Custom de/serialization functions for Rust's [serde](https://serde.rs) + +[![docs.rs badge](https://docs.rs/serde_with/badge.svg)](https://docs.rs/serde_with/) +[![crates.io badge](https://img.shields.io/crates/v/serde_with.svg)](https://crates.io/crates/serde_with/) +[![Build Status](https://travis-ci.org/jonasbb/serde_with.svg?branch=master)](https://travis-ci.org/jonasbb/serde_with) +[![codecov](https://codecov.io/gh/jonasbb/serde_with/branch/master/graph/badge.svg)](https://codecov.io/gh/jonasbb/serde_with) + +--- + +This crate provides custom de/serialization helpers to use in combination with [serde's with-annotation][with-annotation]. + +Serde tracks a wishlist of similar helpers at [serde#553]. + +## Usage + +Add this to your `Cargo.toml`: + +```toml +[dependencies.serde_with] +version = "1.4.0" +features = [ "..." ] +``` + +The crate is divided into different modules. +They contain helpers for external crates and must be enabled with the correspondig feature. + +Annotate your struct or enum to enable the custom de/serializer. + +```rust +#[derive(Deserialize, Serialize)] +struct Foo { + #[serde(with = "serde_with::rust::display_fromstr")] + bar: u8, +} +``` + +Most helpers implement both deserialize and serialize. +If you do not want to derive both, you can simply derive only the necessary parts. +If you want to mix different helpers, you can write your annotations like + +```rust +#[derive(Deserialize, Serialize)] +struct Foo { + #[serde( + deserialize_with = "serde_with::rust::display_fromstr::deserialize", + serialize_with = "serde_with::json::nested::serialize" + )] + bar: u8, +} +``` + +However, this will prohibit you from applying deserialize on the value returned by serializing a struct. + +## Attributes + +The crate comes with custom attributes, which futher extend how serde serialization can be customized. +They are enabled by default, but can be disabled, by removing the default features from this crate. + +The `serde_with` crate re-exports all items from `serde_with_macros`. +This means, if you want to use any proc_macros, import them like `use serde_with::skip_serializing_none`. + +[The documentation for the custom attributes can be found here.](serde_with_macros) + +[with-annotation]: https://serde.rs/field-attrs.html#serdewith--module +[serde#553]: https://github.com/serde-rs/serde/issues/553 + +## License + +Licensed under either of + +* Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) +* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +## Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. diff --git a/third_party/rust/serde_with_macros/src/lib.rs b/third_party/rust/serde_with_macros/src/lib.rs new file mode 100644 index 0000000000..3975aff6ff --- /dev/null +++ b/third_party/rust/serde_with_macros/src/lib.rs @@ -0,0 +1,282 @@ +#![deny( + missing_debug_implementations, + missing_copy_implementations, + trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + variant_size_differences +)] +#![cfg_attr(feature = "cargo-clippy", allow(renamed_and_removed_lints))] +#![doc(html_root_url = "https://docs.rs/serde_with_macros/1.1.0")] + +//! proc-macro extensions for [`serde_with`] +//! +//! This crate should not be used alone, but through the [`serde_with`] crate. +//! +//! [`serde_with`]: https://crates.io/crates/serde_with/ + +extern crate proc_macro; +extern crate proc_macro2; +extern crate quote; +extern crate syn; + +use proc_macro::TokenStream; +use proc_macro2::Span; +use quote::quote; +use syn::{ + parse::Parser, Attribute, Error, Field, Fields, ItemEnum, ItemStruct, Meta, NestedMeta, Path, + Type, +}; + +/// Add `skip_serializing_if` annotations to [`Option`] fields. +/// +/// The attribute can be added to structs and enums. +/// +/// Import this attribute with `use serde_with::skip_serializing_none;`. +/// +/// # Example +/// +/// JSON APIs sometimes have many optional values. +/// Missing values should not be serialized, to keep the serialized format smaller. +/// Such a data type might look like: +/// +/// ```rust +/// # extern crate serde; +/// # use serde::Serialize; +/// # +/// #[derive(Serialize)] +/// struct Data { +/// #[serde(skip_serializing_if = "Option::is_none")] +/// a: Option<String>, +/// #[serde(skip_serializing_if = "Option::is_none")] +/// b: Option<u64>, +/// #[serde(skip_serializing_if = "Option::is_none")] +/// c: Option<String>, +/// #[serde(skip_serializing_if = "Option::is_none")] +/// d: Option<bool>, +/// } +/// ``` +/// +/// The `skip_serializing_if` annotation is repetitive and harms readability. +/// Instead the same struct can be written as: +/// +/// ```rust +/// # extern crate serde; +/// # extern crate serde_with_macros; +/// # +/// # use serde::Serialize; +/// # use serde_with_macros::skip_serializing_none; +/// #[skip_serializing_none] +/// #[derive(Serialize)] +/// struct Data { +/// a: Option<String>, +/// b: Option<u64>, +/// c: Option<String>, +/// d: Option<bool>, +/// } +/// ``` +/// +/// Existing `skip_serializing_if` annotations will not be altered. +/// +/// If some values should always be serialized, then the `serialize_always` can be used. +/// +/// # Limitations +/// +/// The `serialize_always` cannot be used together with a manual `skip_serializing_if` annotations, as these conflict in their meaning. +/// A compile error will be generated if this occurs. +/// +/// The `skip_serializing_none` only works if the type is called [`Option`], [`std::option::Option`], or [`core::option::Option`]. +/// Type aliasing an [`Option`] and giving it another name, will cause this field to be ignored. +/// This cannot be supported, as proc-macros run before type checking, thus it is not possible to determine if a type alias refers to an [`Option`]. +/// +/// ```rust,ignore +/// # extern crate serde; +/// # extern crate serde_with_macros; +/// # +/// # use serde::Serialize; +/// # use serde_with_macros::skip_serializing_none; +/// type MyOption<T> = Option<T>; +/// +/// #[skip_serializing_none] +/// #[derive(Serialize)] +/// struct Data { +/// a: MyOption<String>, // This field will not be skipped +/// } +/// ``` +/// +/// Likewise, if you import a type and name it `Option`, the `skip_serializing_if` attributes will be added and compile errors will occur, if `Option::is_none` is not a valid function. +/// Here the function `Vec::is_none` does not exist and therefore the example fails to compile. +/// +/// ```rust,compile_fail +/// # extern crate serde; +/// # extern crate serde_with_macros; +/// # +/// # use serde::Serialize; +/// # use serde_with_macros::skip_serializing_none; +/// use std::vec::Vec as Option; +/// +/// #[skip_serializing_none] +/// #[derive(Serialize)] +/// struct Data { +/// a: Option<String>, +/// } +/// ``` +/// +#[proc_macro_attribute] +pub fn skip_serializing_none(_args: TokenStream, input: TokenStream) -> TokenStream { + let res = match skip_serializing_none_do(input) { + Ok(res) => res, + Err(msg) => { + let span = Span::call_site(); + Error::new(span, msg).to_compile_error() + } + }; + TokenStream::from(res) +} + +fn skip_serializing_none_do(input: TokenStream) -> Result<proc_macro2::TokenStream, String> { + // For each field in the struct given by `input`, add the `skip_serializing_if` attribute, + // if and only if, it is of type `Option` + if let Ok(mut input) = syn::parse::<ItemStruct>(input.clone()) { + skip_serializing_none_handle_fields(&mut input.fields)?; + Ok(quote!(#input)) + } else if let Ok(mut input) = syn::parse::<ItemEnum>(input) { + input + .variants + .iter_mut() + .map(|variant| skip_serializing_none_handle_fields(&mut variant.fields)) + .collect::<Result<(), _>>()?; + Ok(quote!(#input)) + } else { + Err("The attribute can only be applied to struct or enum definitions.".into()) + } +} + +/// Return `true`, if the type path refers to `std::option::Option` +/// +/// Accepts +/// +/// * `Option` +/// * `std::option::Option`, with or without leading `::` +/// * `core::option::Option`, with or without leading `::` +fn is_std_option(path: &Path) -> bool { + (path.leading_colon.is_none() && path.segments.len() == 1 && path.segments[0].ident == "Option") + || (path.segments.len() == 3 + && (path.segments[0].ident == "std" || path.segments[0].ident == "core") + && path.segments[1].ident == "option" + && path.segments[2].ident == "Option") +} + +/// Determine if the `field` has an attribute with given `namespace` and `name` +/// +/// On the example of +/// `#[serde(skip_serializing_if = "Option::is_none")]` +// +/// * `serde` is the outermost path, here namespace +/// * it contains a Meta::List +/// * which contains in another Meta a Meta::NameValue +/// * with the name being `skip_serializing_if` +#[cfg_attr(feature = "cargo-clippy", allow(cmp_owned))] +fn field_has_attribute(field: &Field, namespace: &str, name: &str) -> bool { + // On the example of + // #[serde(skip_serializing_if = "Option::is_none")] + // + // `serde` is the outermost path, here namespace + // it contains a Meta::List + // which contains in another Meta a Meta::NameValue + // with the name being `skip_serializing_if` + + for attr in &field.attrs { + if attr.path.is_ident(namespace) { + // Ignore non parsable attributes, as these are not important for us + if let Ok(expr) = attr.parse_meta() { + if let Meta::List(expr) = expr { + for expr in expr.nested { + if let NestedMeta::Meta(Meta::NameValue(expr)) = expr { + if let Some(ident) = expr.path.get_ident() { + if ident.to_string() == name { + return true; + } + } + } + } + } + } + } + } + false +} + +/// Add the skip_serializing_if annotation to each field of the struct +fn skip_serializing_none_add_attr_to_field<'a>( + fields: impl IntoIterator<Item = &'a mut Field>, +) -> Result<(), String> { + fields.into_iter().map(|field| ->Result<(), String> { + if let Type::Path(path) = &field.ty.clone() { + if is_std_option(&path.path) { + let has_skip_serializing_if = + field_has_attribute(&field, "serde", "skip_serializing_if"); + + // Remove the `serialize_always` attribute + let mut has_always_attr = false; + field.attrs.retain(|attr| { + let has_attr = attr.path.is_ident("serialize_always"); + has_always_attr |= has_attr; + !has_attr + }); + + // Error on conflicting attributes + if has_always_attr && has_skip_serializing_if { + let mut msg = r#"The attributes `serialize_always` and `serde(skip_serializing_if = "...")` cannot be used on the same field"#.to_string(); + if let Some(ident) = &field.ident { + msg += ": `"; + msg += &ident.to_string(); + msg += "`"; + } + msg +="."; + return Err(msg); + } + + // Do nothing if `skip_serializing_if` or `serialize_always` is already present + if has_skip_serializing_if || has_always_attr { + return Ok(()); + } + + // Add the `skip_serializing_if` attribute + let attr_tokens = quote!( + #[serde(skip_serializing_if = "Option::is_none")] + ); + let parser = Attribute::parse_outer; + let attrs = parser + .parse2(attr_tokens) + .expect("Static attr tokens should not panic"); + field.attrs.extend(attrs); + } else { + // Warn on use of `serialize_always` on non-Option fields + let has_attr= field.attrs.iter().any(|attr| { + attr.path.is_ident("serialize_always") + }); + if has_attr { + return Err("`serialize_always` may only be used on fields of type `Option`.".into()); + } + } + } + Ok(()) + }).collect() +} + +/// Handle a single struct or a single enum variant +fn skip_serializing_none_handle_fields(fields: &mut Fields) -> Result<(), String> { + match fields { + // simple, no fields, do nothing + Fields::Unit => Ok(()), + Fields::Named(ref mut fields) => { + skip_serializing_none_add_attr_to_field(fields.named.iter_mut()) + } + Fields::Unnamed(ref mut fields) => { + skip_serializing_none_add_attr_to_field(fields.unnamed.iter_mut()) + } + } +} diff --git a/third_party/rust/serde_with_macros/tests/compile-fail/skip-none-always.rs b/third_party/rust/serde_with_macros/tests/compile-fail/skip-none-always.rs new file mode 100644 index 0000000000..b52272439b --- /dev/null +++ b/third_party/rust/serde_with_macros/tests/compile-fail/skip-none-always.rs @@ -0,0 +1,30 @@ +extern crate serde; +extern crate serde_with_macros; + +use serde::Serialize; +use serde_with_macros::skip_serializing_none; + +#[skip_serializing_none] +#[derive(Serialize)] +struct Data { + #[serialize_always] + #[serde(skip_serializing_if = "Option::is_none")] + a: Option<char>, +} + +#[skip_serializing_none] +#[derive(Serialize)] +struct Data2( + #[serialize_always] + #[serde(skip_serializing_if = "Option::is_none")] + Option<char>, +); + +#[skip_serializing_none] +#[derive(Serialize)] +struct Data3 { + #[serialize_always] + a: char, +} + +fn main() {} diff --git a/third_party/rust/serde_with_macros/tests/compile-fail/skip-none-always.stderr b/third_party/rust/serde_with_macros/tests/compile-fail/skip-none-always.stderr new file mode 100644 index 0000000000..226d7b55c8 --- /dev/null +++ b/third_party/rust/serde_with_macros/tests/compile-fail/skip-none-always.stderr @@ -0,0 +1,17 @@ +error: The attributes `serialize_always` and `serde(skip_serializing_if = "...")` cannot be used on the same field: `a`. + --> $DIR/skip-none-always.rs:7:1 + | +7 | #[skip_serializing_none] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: The attributes `serialize_always` and `serde(skip_serializing_if = "...")` cannot be used on the same field. + --> $DIR/skip-none-always.rs:15:1 + | +15 | #[skip_serializing_none] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `serialize_always` may only be used on fields of type `Option`. + --> $DIR/skip-none-always.rs:23:1 + | +23 | #[skip_serializing_none] + | ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/third_party/rust/serde_with_macros/tests/compile-fail/skip-none-not-struct.rs b/third_party/rust/serde_with_macros/tests/compile-fail/skip-none-not-struct.rs new file mode 100644 index 0000000000..f6776db778 --- /dev/null +++ b/third_party/rust/serde_with_macros/tests/compile-fail/skip-none-not-struct.rs @@ -0,0 +1,8 @@ +extern crate serde_with_macros; + +use serde_with_macros::skip_serializing_none; + +#[skip_serializing_none] +fn test() {} + +fn main() {} diff --git a/third_party/rust/serde_with_macros/tests/compile-fail/skip-none-not-struct.stderr b/third_party/rust/serde_with_macros/tests/compile-fail/skip-none-not-struct.stderr new file mode 100644 index 0000000000..479fa7ea75 --- /dev/null +++ b/third_party/rust/serde_with_macros/tests/compile-fail/skip-none-not-struct.stderr @@ -0,0 +1,5 @@ +error: The attribute can only be applied to struct or enum definitions. + --> $DIR/skip-none-not-struct.rs:5:1 + | +5 | #[skip_serializing_none] + | ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/third_party/rust/serde_with_macros/tests/compiler-messages.rs b/third_party/rust/serde_with_macros/tests/compiler-messages.rs new file mode 100644 index 0000000000..01373a3d3b --- /dev/null +++ b/third_party/rust/serde_with_macros/tests/compiler-messages.rs @@ -0,0 +1,15 @@ +extern crate rustversion; +extern crate trybuild; + +// This test fails for older compiler versions since the error messages are different. +#[rustversion::attr(before(1.35), ignore)] +#[test] +fn compile_test() { + // This test does not work under tarpaulin, so skip it if detected + if std::env::var("TARPAULIN") == Ok("1".to_string()) { + return; + } + + let t = trybuild::TestCases::new(); + t.compile_fail("tests/compile-fail/*.rs"); +} diff --git a/third_party/rust/serde_with_macros/tests/skip_serializing_null.rs b/third_party/rust/serde_with_macros/tests/skip_serializing_null.rs new file mode 100644 index 0000000000..7fb43bc923 --- /dev/null +++ b/third_party/rust/serde_with_macros/tests/skip_serializing_null.rs @@ -0,0 +1,154 @@ +extern crate pretty_assertions; +extern crate serde; +extern crate serde_json; +extern crate serde_with_macros; + +use pretty_assertions::assert_eq; +use serde::{Deserialize, Serialize}; +use serde_json::json; +use serde_with_macros::skip_serializing_none; + +macro_rules! test { + ($fn:ident, $struct:ident) => { + #[test] + fn $fn() { + let expected = json!({}); + let data = $struct { + a: None, + b: None, + c: None, + d: None, + }; + let res = serde_json::to_value(&data).unwrap(); + assert_eq!(expected, res); + assert_eq!(data, serde_json::from_value(res).unwrap()); + } + }; +} + +macro_rules! test_tuple { + ($fn:ident, $struct:ident) => { + #[test] + fn $fn() { + let expected = json!([]); + let data = $struct(None, None); + let res = serde_json::to_value(&data).unwrap(); + assert_eq!(expected, res); + } + }; +} + +#[skip_serializing_none] +#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] +struct DataBasic { + a: Option<String>, + b: Option<String>, + c: Option<String>, + d: Option<String>, +} +test!(test_basic, DataBasic); + +#[skip_serializing_none] +#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] +struct DataFullyQualified { + a: ::std::option::Option<String>, + b: std::option::Option<String>, + c: ::std::option::Option<i64>, + d: core::option::Option<String>, +} +test!(test_fully_qualified, DataFullyQualified); + +fn never<T>(_t: &T) -> bool { + false +} + +#[skip_serializing_none] +#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] +struct DataExistingAnnotation { + #[serde(skip_serializing_if = "Option::is_none")] + a: Option<String>, + #[serde(default, skip_serializing_if = "Option::is_none", rename = "abc")] + b: Option<String>, + #[serde(default)] + c: Option<String>, + #[serde(skip_serializing_if = "never")] + #[serde(rename = "name")] + d: Option<String>, +} + +#[test] +fn test_existing_annotation() { + let expected = json!({ "name": null }); + let data = DataExistingAnnotation { + a: None, + b: None, + c: None, + d: None, + }; + let res = serde_json::to_value(&data).unwrap(); + assert_eq!(expected, res); + assert_eq!(data, serde_json::from_value(res).unwrap()); +} + +#[skip_serializing_none] +#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] +struct DataSerializeAlways { + #[serialize_always] + a: Option<String>, + #[serialize_always] + b: Option<String>, + c: i64, + #[serialize_always] + d: Option<String>, +} + +#[test] +fn test_serialize_always() { + let expected = json!({ + "a": null, + "b": null, + "c": 0, + "d": null + }); + let data = DataSerializeAlways { + a: None, + b: None, + c: 0, + d: None, + }; + let res = serde_json::to_value(&data).unwrap(); + assert_eq!(expected, res); + assert_eq!(data, serde_json::from_value(res).unwrap()); +} + +#[skip_serializing_none] +#[derive(Debug, Eq, PartialEq, Serialize)] +struct DataTuple(Option<String>, std::option::Option<String>); +test_tuple!(test_tuple, DataTuple); + +#[skip_serializing_none] +#[derive(Debug, Eq, PartialEq, Serialize)] +enum DataEnum { + Tuple(Option<i64>, std::option::Option<bool>), + Struct { + a: Option<String>, + b: Option<String>, + }, +} + +#[test] +fn test_enum() { + let expected = json!({ + "Tuple": [] + }); + let data = DataEnum::Tuple(None, None); + let res = serde_json::to_value(&data).unwrap(); + assert_eq!(expected, res); + + let expected = json!({ + "Struct": {} + }); + let data = DataEnum::Struct { a: None, b: None }; + let res = serde_json::to_value(&data).unwrap(); + assert_eq!(expected, res); +} diff --git a/third_party/rust/serde_with_macros/tests/version_numbers.rs b/third_party/rust/serde_with_macros/tests/version_numbers.rs new file mode 100644 index 0000000000..9cbd3b3a5a --- /dev/null +++ b/third_party/rust/serde_with_macros/tests/version_numbers.rs @@ -0,0 +1,6 @@ +extern crate version_sync; + +#[test] +fn test_html_root_url() { + version_sync::assert_html_root_url_updated!("src/lib.rs"); +} |