diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /third_party/rust/smart-default | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/smart-default')
-rw-r--r-- | third_party/rust/smart-default/.cargo-checksum.json | 1 | ||||
-rw-r--r-- | third_party/rust/smart-default/CHANGELOG.md | 59 | ||||
-rw-r--r-- | third_party/rust/smart-default/Cargo.lock | 47 | ||||
-rw-r--r-- | third_party/rust/smart-default/Cargo.toml | 34 | ||||
-rw-r--r-- | third_party/rust/smart-default/LICENSE | 21 | ||||
-rw-r--r-- | third_party/rust/smart-default/README.md | 39 | ||||
-rw-r--r-- | third_party/rust/smart-default/examples/example.rs | 33 | ||||
-rw-r--r-- | third_party/rust/smart-default/src/body_impl.rs | 158 | ||||
-rw-r--r-- | third_party/rust/smart-default/src/default_attr.rs | 89 | ||||
-rw-r--r-- | third_party/rust/smart-default/src/lib.rs | 73 | ||||
-rw-r--r-- | third_party/rust/smart-default/src/util.rs | 21 | ||||
-rw-r--r-- | third_party/rust/smart-default/tests/tests.rs | 170 |
12 files changed, 745 insertions, 0 deletions
diff --git a/third_party/rust/smart-default/.cargo-checksum.json b/third_party/rust/smart-default/.cargo-checksum.json new file mode 100644 index 0000000000..c48bc2cde9 --- /dev/null +++ b/third_party/rust/smart-default/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"CHANGELOG.md":"391b5ca7965b379588e8cdd0fdc76a2c6ac44603dd007ddfb9263dc4232b6eb4","Cargo.lock":"6060bc53da766a9073b1fd836699f99b2fc28a99bf15dbf9f213f6bce3008dba","Cargo.toml":"886220167526d72040ace0629cab35e9e7a2b22d76bb28c7332a2e9ab2e42db8","LICENSE":"f09111a2bf85257f1d098de0095ad77fd12649fbbe9a33c8a13e422848b601bb","README.md":"553e1508b8e7b3fc0afeb8636263169da6a85d92aa6605fbeada927f62e6a88e","examples/example.rs":"5ea8b9f063549e46e74a8b5e7079a14a8296d945536271d1be65400a9377218b","src/body_impl.rs":"d1e3a209037d43ad653fdf7628ad52c4b70eced8d03e5e7b12e4381de8eb642a","src/default_attr.rs":"89e721794c16da3dbf59fc71381cbff74b7c0b5a5805765306fdfda0a9615ec2","src/lib.rs":"ff620a9442205980f0445f1624dba132f710ba5fcaccd65b8d1493f8af232333","src/util.rs":"8c553bfcf7d8a7d7960a1c3e3517218648ab4bd3ec5144b972a623b98bff1427","tests/tests.rs":"2f847e22064723d70145db3dfeadfa5b14b0d58ba9bdc36ff0e46b7f52eba511"},"package":"0eb01866308440fc64d6c44d9e86c5cc17adfe33c4d6eed55da9145044d0ffc1"}
\ No newline at end of file diff --git a/third_party/rust/smart-default/CHANGELOG.md b/third_party/rust/smart-default/CHANGELOG.md new file mode 100644 index 0000000000..07b4a571ad --- /dev/null +++ b/third_party/rust/smart-default/CHANGELOG.md @@ -0,0 +1,59 @@ +# 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] + +## 0.7.1 - 2023-04-24 +### Fixed +- Fixed bug where the macro fails on valid default expression that is also a + valid attribute meta because it was expecting the `_code` hack. + +## 0.7.0 - 2023-04-23 +### Changed +- Update `syn` to version 2 + +## 0.6.0 - 2019-12-13 +### Changed +- Update `syn`, `quote` and `proc-macro2` versions to `1.*.*`. + +## 0.5.2 - 2019-04-13 +### Fixed +- Omit linting of generated code by adding `#[automatically_derived]` attribute. + +## 0.5.1 - 2019-03-01 +### Fixed +- Don't use a multi-pattern `if let`, as this unnecessarily requires version + 1.33.0 of Rust. + +## 0.5.0 - 2019-03-01 +### Changed +- When the default is a string literal, strap an `.into()` after it to + automatically convert it to `String` when needed. + +## 0.4.0 - 2019-02-19 +### Added +- `#[default(_code = "...")]` syntax for defaults that cannot be parsed as + attributes no matter what. + +## 0.3.0 - 2018-11-02 +### Changed +- Require Rust 1.30+. +- Use direct attribute value instead of having to wrap them in strings. +- Moved the docs from the module level to the custom derive. + +### Added +- `#[default(...)]` syntax in addition to `#[default = ...]`. This is required + to deal with some parsing limitations. + +## 0.2.0 - 2017-08-21 +### Added +- Support generic types. +- Generate doc for the trait impl that describes the default values. + +## 0.1.0 - 2017-08-18 +### Added +- Custom derive `SmartDefault` for implementing the `Default` trait. +- `#[default = ...]` attribute on fields for smart-default. diff --git a/third_party/rust/smart-default/Cargo.lock b/third_party/rust/smart-default/Cargo.lock new file mode 100644 index 0000000000..e797d66e01 --- /dev/null +++ b/third_party/rust/smart-default/Cargo.lock @@ -0,0 +1,47 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "proc-macro2" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "smart-default" +version = "0.7.1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "2.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" diff --git a/third_party/rust/smart-default/Cargo.toml b/third_party/rust/smart-default/Cargo.toml new file mode 100644 index 0000000000..ae15d56ee7 --- /dev/null +++ b/third_party/rust/smart-default/Cargo.toml @@ -0,0 +1,34 @@ +# 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 are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +name = "smart-default" +version = "0.7.1" +authors = ["IdanArye <idanarye@gmail.com>"] +description = "Rust custom-derive macro for Default with more control on the fields" +documentation = "https://idanarye.github.io/rust-smart-default/" +readme = "README.md" +keywords = ["default"] +license = "MIT" +repository = "https://github.com/idanarye/rust-smart-default" + +[lib] +proc-macro = true + +[dependencies.proc-macro2] +version = "1" + +[dependencies.quote] +version = "1" + +[dependencies.syn] +version = "2" diff --git a/third_party/rust/smart-default/LICENSE b/third_party/rust/smart-default/LICENSE new file mode 100644 index 0000000000..015f0b2fcc --- /dev/null +++ b/third_party/rust/smart-default/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Idan Arye + +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/smart-default/README.md b/third_party/rust/smart-default/README.md new file mode 100644 index 0000000000..1ece6950c4 --- /dev/null +++ b/third_party/rust/smart-default/README.md @@ -0,0 +1,39 @@ +[![Build Status](https://github.com/idanarye/rust-smart-default/workflows/CI/badge.svg)](https://github.com/idanarye/rust-smart-default/actions) +[![Latest Version](https://img.shields.io/crates/v/smart-default.svg)](https://crates.io/crates/smart-default) +[![Rust Documentation](https://img.shields.io/badge/api-rustdoc-blue.svg)](https://idanarye.github.io/rust-smart-default/) + +# Rust SmartDefault + +Custom derive for automatically implementing the `Default` trait with customized default values: + +```rust +use smart_default::SmartDefault; + +#[derive(SmartDefault)] +enum Foo { + Bar, + #[default] + Baz { + #[default = 12] + a: i32, + b: i32, + #[default(Some(Default::default()))] + c: Option<i32>, + #[default(_code = "vec![1, 2, 3]")] + d: Vec<u32>, + #[default = "four"] + e: String, + }, + Qux(i32), +} + +assert!(Foo::default() == Foo::Baz { + a: 12, + b: 0, + c: Some(0), + d: vec![1, 2, 3], + e: "four".to_owned(), +}); +``` + +Requires Rust 1.30+ (for non-string values in attributes) diff --git a/third_party/rust/smart-default/examples/example.rs b/third_party/rust/smart-default/examples/example.rs new file mode 100644 index 0000000000..ee3473daca --- /dev/null +++ b/third_party/rust/smart-default/examples/example.rs @@ -0,0 +1,33 @@ +use smart_default::SmartDefault; + +#[derive(PartialEq, SmartDefault, Debug)] +#[allow(dead_code)] +enum Foo { + Bar, + #[default] + Baz { + #[default(12)] + a: i32, + b: i32, + #[default(Some(Default::default()))] + c: Option<i32>, + #[default(_code = "vec![1, 2, 3]")] + d: Vec<u32>, + #[default = "four"] + e: String, + }, + Qux(i32), +} + +fn main() { + assert!( + Foo::default() + == Foo::Baz { + a: 12, + b: 0, + c: Some(0), + d: vec![1, 2, 3], + e: "four".to_owned(), + } + ); +} diff --git a/third_party/rust/smart-default/src/body_impl.rs b/third_party/rust/smart-default/src/body_impl.rs new file mode 100644 index 0000000000..09b80e56c3 --- /dev/null +++ b/third_party/rust/smart-default/src/body_impl.rs @@ -0,0 +1,158 @@ +use proc_macro2::TokenStream; + +use quote::quote; +use syn::parse::Error; +use syn::spanned::Spanned; +use syn::DeriveInput; + +use crate::default_attr::{ConversionStrategy, DefaultAttr}; +use crate::util::find_only; + +pub fn impl_my_derive(input: &DeriveInput) -> Result<TokenStream, Error> { + let name = &input.ident; + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + + let (default_expr, doc) = match input.data { + syn::Data::Struct(ref body) => { + let (body_assignment, doc) = default_body_tt(&body.fields)?; + ( + quote! { + #name #body_assignment + }, + format!("Return `{}{}`", name, doc), + ) + } + syn::Data::Enum(ref body) => { + let default_variant = find_only(body.variants.iter(), |variant| { + if let Some(meta) = DefaultAttr::find_in_attributes(&variant.attrs)? { + if meta.code.is_none() { + Ok(true) + } else { + Err(Error::new( + meta.code.span(), + "Attribute #[default] on variants should have no value", + )) + } + } else { + Ok(false) + } + })? + .ok_or_else(|| Error::new(input.span(), "No default variant"))?; + let default_variant_name = &default_variant.ident; + let (body_assignment, doc) = default_body_tt(&default_variant.fields)?; + ( + quote! { + #name :: #default_variant_name #body_assignment + }, + format!("Return `{}::{}{}`", name, default_variant_name, doc), + ) + } + syn::Data::Union(_) => { + panic!() + } + }; + Ok(quote! { + #[automatically_derived] + impl #impl_generics Default for #name #ty_generics #where_clause { + #[doc = #doc] + fn default() -> Self { + #default_expr + } + } + }) +} + +/// Return a token-tree for the default "body" - the part after the name that contains the values. +/// That is, the `{ ... }` part for structs, the `(...)` part for tuples, and nothing for units. +fn default_body_tt(body: &syn::Fields) -> Result<(TokenStream, String), Error> { + let mut doc = String::new(); + use std::fmt::Write; + let body_tt = match body { + syn::Fields::Named(ref fields) => { + doc.push_str(" {"); + let result = { + let field_assignments = fields + .named + .iter() + .map(|field| { + let field_name = field.ident.as_ref(); + let (default_value, default_doc) = field_default_expr_and_doc(field)?; + write!( + &mut doc, + "\n {}: {},", + field_name.expect("field value in struct is empty"), + default_doc + ) + .unwrap(); + // let default_value = default_value.into_token_stream(); + Ok(quote! { #field_name : #default_value }) + }) + .collect::<Result<Vec<_>, Error>>()?; + quote! { + { + #( #field_assignments ),* + } + } + }; + if doc.ends_with(',') { + doc.pop(); + doc.push('\n'); + }; + doc.push('}'); + result + } + syn::Fields::Unnamed(ref fields) => { + doc.push('('); + let result = { + let field_assignments = fields + .unnamed + .iter() + .map(|field| { + let (default_value, default_doc) = field_default_expr_and_doc(field)?; + write!(&mut doc, "{}, ", default_doc).unwrap(); + Ok(default_value) + }) + .collect::<Result<Vec<TokenStream>, Error>>()?; + quote! { + ( + #( #field_assignments ),* + ) + } + }; + if doc.ends_with(", ") { + doc.pop(); + doc.pop(); + }; + doc.push(')'); + result + } + &syn::Fields::Unit => quote! {}, + }; + Ok((body_tt, doc)) +} + +/// Return a default expression for a field based on it's `#[default = "..."]` attribute. Panic +/// if there is more than one, of if there is a `#[default]` attribute without value. +fn field_default_expr_and_doc(field: &syn::Field) -> Result<(TokenStream, String), Error> { + if let Some(default_attr) = DefaultAttr::find_in_attributes(&field.attrs)? { + let conversion_strategy = default_attr.conversion_strategy(); + let field_value = default_attr.code.ok_or_else(|| { + Error::new(field.span(), "Expected #[default = ...] or #[default(...)]") + })?; + + let field_value = match conversion_strategy { + ConversionStrategy::NoConversion => field_value, + ConversionStrategy::Into => quote!((#field_value).into()), + }; + + let field_doc = format!("{}", field_value); + Ok((field_value, field_doc)) + } else { + Ok(( + quote! { + Default::default() + }, + "Default::default()".to_owned(), + )) + } +} diff --git a/third_party/rust/smart-default/src/default_attr.rs b/third_party/rust/smart-default/src/default_attr.rs new file mode 100644 index 0000000000..ce871fd231 --- /dev/null +++ b/third_party/rust/smart-default/src/default_attr.rs @@ -0,0 +1,89 @@ +use proc_macro2::TokenStream; +use quote::ToTokens; +use syn::{parse::Error, MetaNameValue}; + +use crate::util::find_only; + +#[derive(Debug, Clone, Copy)] +pub enum ConversionStrategy { + NoConversion, + Into, +} + +pub struct DefaultAttr { + pub code: Option<TokenStream>, + conversion_strategy: Option<ConversionStrategy>, +} + +impl DefaultAttr { + pub fn find_in_attributes(attrs: &[syn::Attribute]) -> Result<Option<Self>, Error> { + if let Some(default_attr) = + find_only(attrs.iter(), |attr| Ok(attr.path().is_ident("default")))? + { + match &default_attr.meta { + syn::Meta::Path(_) => Ok(Some(Self { + code: None, + conversion_strategy: None, + })), + syn::Meta::List(meta) => { + // If the meta contains exactly (_code = "...") take the string literal as the + // expression + if let Ok(ParseCodeHack(code_hack)) = syn::parse(meta.tokens.clone().into()) { + Ok(Some(Self { + code: Some(code_hack), + conversion_strategy: Some(ConversionStrategy::NoConversion), + })) + } else { + Ok(Some(Self { + code: Some(meta.tokens.clone()), + conversion_strategy: None, + })) + } + } + syn::Meta::NameValue(MetaNameValue { value, .. }) => Ok(Some(Self { + code: Some(value.into_token_stream()), + conversion_strategy: None, + })), + } + } else { + Ok(None) + } + } + + pub fn conversion_strategy(&self) -> ConversionStrategy { + if let Some(conversion_strategy) = self.conversion_strategy { + // Conversion strategy already set + return conversion_strategy; + } + let code = if let Some(code) = &self.code { + code + } else { + // #[default] - so no conversion (`Default::default()` already has the correct type) + return ConversionStrategy::NoConversion; + }; + match syn::parse::<syn::Lit>(code.clone().into()) { + Ok(syn::Lit::Str(_)) | Ok(syn::Lit::ByteStr(_)) => { + // A string literal - so we need a conversion in case we need to make it a `String` + return ConversionStrategy::Into; + } + _ => {} + } + // Not handled by one of the rules, so we don't convert it to avoid causing trouble + ConversionStrategy::NoConversion + } +} + +struct ParseCodeHack(TokenStream); + +impl syn::parse::Parse for ParseCodeHack { + fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { + let ident: syn::Ident = input.parse()?; + if ident != "_code" { + return Err(Error::new(ident.span(), "Expected `_code`")); + } + input.parse::<syn::token::Eq>()?; + let code: syn::LitStr = input.parse()?; + let code: TokenStream = code.parse()?; + Ok(ParseCodeHack(code)) + } +} diff --git a/third_party/rust/smart-default/src/lib.rs b/third_party/rust/smart-default/src/lib.rs new file mode 100644 index 0000000000..360828f1ef --- /dev/null +++ b/third_party/rust/smart-default/src/lib.rs @@ -0,0 +1,73 @@ +use syn::{parse_macro_input, DeriveInput}; + +mod body_impl; +mod default_attr; +mod util; + +/// # Smart Default +/// +/// This crate provides a custom derive for `SmartDefault`. `SmartDefault` is not a real trait - +/// deriving it will actually `impl Default`. The difference from regular `#[derive(Default)]` is +/// that `#[derive(SmartDefault)]` allows you to use `#[default = "..."]` attributes to customize +/// the `::default()` method and to support `struct`s that don't have `Default` for all their +/// fields - and even `enum`s! +/// +/// # Examples +/// +/// ``` +/// use smart_default::SmartDefault; +/// +/// # fn main() { +/// #[derive(SmartDefault)] +/// # #[derive(PartialEq)] +/// # #[allow(dead_code)] +/// enum Foo { +/// Bar, +/// #[default] +/// Baz { +/// #[default = 12] +/// a: i32, +/// b: i32, +/// #[default(Some(Default::default()))] +/// c: Option<i32>, +/// #[default(_code = "vec![1, 2, 3]")] +/// d: Vec<u32>, +/// #[default = "four"] +/// e: String, +/// }, +/// Qux(i32), +/// } +/// +/// assert!(Foo::default() == Foo::Baz { +/// a: 12, +/// b: 0, +/// c: Some(0), +/// d: vec![1, 2, 3], +/// e: "four".to_owned(), +/// }); +/// # } +/// ``` +/// +/// * `Baz` has the `#[default]` attribute. This means that the default `Foo` is a `Foo::Baz`. Only +/// one variant may have a `#[default]` attribute, and that attribute must have no value. +/// * `a` has a `#[default = 12]` attribute. This means that it's default value is `12`. +/// * `b` has no `#[default = ...]` attribute. It's default value will `i32`'s default value +/// instead - `0`. +/// * `c` is an `Option<i32>`, and it's default is `Some(Default::default())`. Rust cannot (currently) +/// parse `#[default = Some(Default::default())]` and therefore we have to use a special syntax: +/// `#[default(Some(Default::default))]` +/// * `d` has the `!` token in it, which cannot (currently) be parsed even with `#[default(...)]`, +/// so we have to encode it as a string and mark it as `_code = `. +/// * `e` is a `String`, so the string literal "four" is automatically converted to it. This +/// automatic conversion **only** happens to string (or byte string) literals - and only if +/// `_code` is not used. +/// * Documentation for the `impl Default` section is generated automatically, specifying the +/// default value returned from `::default()`. +#[proc_macro_derive(SmartDefault, attributes(default))] +pub fn derive_smart_default(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = parse_macro_input!(input as DeriveInput); + match body_impl::impl_my_derive(&input) { + Ok(output) => output.into(), + Err(error) => error.to_compile_error().into(), + } +} diff --git a/third_party/rust/smart-default/src/util.rs b/third_party/rust/smart-default/src/util.rs new file mode 100644 index 0000000000..0d4b247bbf --- /dev/null +++ b/third_party/rust/smart-default/src/util.rs @@ -0,0 +1,21 @@ +use syn::parse::Error; +use syn::spanned::Spanned; + +/// Return the value that fulfills the predicate if there is one in the slice. Panic if there is +/// more than one. +pub fn find_only<T, F>(iter: impl Iterator<Item = T>, pred: F) -> Result<Option<T>, Error> +where + T: Spanned, + F: Fn(&T) -> Result<bool, Error>, +{ + let mut result = None; + for item in iter { + if pred(&item)? { + if result.is_some() { + return Err(Error::new(item.span(), "Multiple defaults")); + } + result = Some(item); + } + } + Ok(result) +} diff --git a/third_party/rust/smart-default/tests/tests.rs b/third_party/rust/smart-default/tests/tests.rs new file mode 100644 index 0000000000..423c91df33 --- /dev/null +++ b/third_party/rust/smart-default/tests/tests.rs @@ -0,0 +1,170 @@ +use smart_default::SmartDefault; + +#[test] +fn test_unit() { + #[derive(PartialEq, SmartDefault)] + struct Foo; + + assert!(Foo::default() == Foo); +} + +#[test] +fn test_tuple() { + #[derive(PartialEq, SmartDefault)] + struct Foo( + #[default = 10] i32, + #[default = 20] i32, + // No default + i32, + ); + + assert!(Foo::default() == Foo(10, 20, 0)); +} + +#[test] +fn test_struct() { + #[derive(PartialEq, SmartDefault)] + struct Foo { + #[default = 10] + x: i32, + #[default = 20] + y: i32, + // No default + z: i32, + } + + assert!(Foo::default() == Foo { x: 10, y: 20, z: 0 }); +} + +#[test] +fn test_enum_of_units() { + #[derive(PartialEq, SmartDefault)] + pub enum Foo { + #[allow(dead_code)] + Bar, + #[default] + Baz, + #[allow(dead_code)] + Qux, + } + + assert!(Foo::default() == Foo::Baz); +} + +#[test] +fn test_enum_of_tuples() { + #[derive(PartialEq, SmartDefault)] + pub enum Foo { + #[allow(dead_code)] + Bar(i32), + #[default] + Baz(#[default = 10] i32, i32), + #[allow(dead_code)] + Qux(i32), + } + + assert!(Foo::default() == Foo::Baz(10, 0)); +} + +#[test] +fn test_enum_of_structs() { + #[derive(PartialEq, SmartDefault)] + pub enum Foo { + #[allow(dead_code)] + Bar { x: i32 }, + #[default] + Baz { + #[default = 10] + y: i32, + z: i32, + }, + #[allow(dead_code)] + Qux { w: i32 }, + } + + assert!(Foo::default() == Foo::Baz { y: 10, z: 0 }); +} + +#[test] +fn test_enum_mixed() { + #[derive(PartialEq, SmartDefault)] + enum Foo { + #[allow(dead_code)] + Bar, + #[default] + Baz(#[default = 10] i32), + #[allow(dead_code)] + Qux { w: i32 }, + } + + assert!(Foo::default() == Foo::Baz(10)); +} + +#[test] +fn test_generics_type_parameters() { + #[derive(PartialEq, SmartDefault)] + struct Foo<T> + where + T: Default, + { + #[default(Some(Default::default()))] + x: Option<T>, + } + + assert!(Foo::default() == Foo { x: Some(0) }); +} + +#[test] +fn test_generics_lifetime_parameters() { + // NOTE: A default value makes no sense with lifetime parameters, since ::default() receives no + // paramters and therefore can receive no lifetimes. But it does make sense if you make a variant + // without ref fields the default. + + #[derive(PartialEq, SmartDefault)] + enum Foo<'a> { + #[default] + Bar(i32), + #[allow(dead_code)] + Baz(&'a str), + } + + assert!(Foo::default() == Foo::Bar(0)); +} + +#[test] +fn test_code_hack() { + #[derive(PartialEq, SmartDefault)] + struct Foo { + #[default(_code = "vec![1, 2, 3]")] + v: Vec<u32>, + } + + assert!(Foo::default().v == [1, 2, 3]); +} + +#[test] +fn test_string_conversion() { + #[derive(PartialEq, SmartDefault)] + struct Foo(#[default = "one"] &'static str, #[default("two")] String); + + assert!(Foo::default() == Foo("one", "two".to_owned())); +} + +#[test] +fn test_non_code_hack_valid_meta() { + #[derive(Debug, PartialEq, SmartDefault)] + struct Foo { + #[default(true)] + bar: bool, + #[default(Option::None)] + baz: Option<()>, + } + + assert_eq!( + Foo::default(), + Foo { + bar: true, + baz: None + } + ); +} |