diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/smart-default/src | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/smart-default/src')
-rwxr-xr-x | third_party/rust/smart-default/src/body_impl.rs | 130 | ||||
-rwxr-xr-x | third_party/rust/smart-default/src/default_attr.rs | 125 | ||||
-rwxr-xr-x | third_party/rust/smart-default/src/lib.rs | 84 | ||||
-rwxr-xr-x | third_party/rust/smart-default/src/util.rs | 29 |
4 files changed, 368 insertions, 0 deletions
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 100755 index 0000000000..a40b11f562 --- /dev/null +++ b/third_party/rust/smart-default/src/body_impl.rs @@ -0,0 +1,130 @@ +use proc_macro2::TokenStream; + +use syn::DeriveInput; +use syn::spanned::Spanned; +use syn::parse::Error; +use quote::quote; + +use default_attr::{DefaultAttr, ConversionStrategy}; +use 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 (&mut 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 (&mut 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 100755 index 0000000000..c429b23bb9 --- /dev/null +++ b/third_party/rust/smart-default/src/default_attr.rs @@ -0,0 +1,125 @@ +use proc_macro2::TokenStream; +use syn::parse::Error; +use syn::spanned::Spanned; +use quote::ToTokens; + +use util::{find_only, single_value}; + +#[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| is_default_attr(attr))? { + match default_attr.parse_meta() { + Ok(syn::Meta::Path(_)) => Ok(Some(Self { + code: None, + conversion_strategy: None, + })), + Ok(syn::Meta::List(meta)) => { + if let Some(field_value) = parse_code_hack(&meta)? { // #[default(_code = "...")] + Ok(Some(Self { + code: Some(field_value.into_token_stream()), + conversion_strategy: Some(ConversionStrategy::NoConversion), + })) + } else if let Some(field_value) = single_value(meta.nested.iter()) { // #[default(...)] + Ok(Some(Self { + code: Some(field_value.into_token_stream()), + conversion_strategy: None, + })) + } else { + return Err(Error::new( + if meta.nested.is_empty() { + meta.span() + } else { + meta.nested.span() + }, + "Expected signle value in #[default(...)]")); + } + } + Ok(syn::Meta::NameValue(meta)) => { + Ok(Some(Self { + code: Some(meta.lit.into_token_stream()), + conversion_strategy: None, + })) + } + Err(error) => { + if let syn::Expr::Paren(as_parens) = syn::parse(default_attr.tokens.clone().into())? { + Ok(Some(Self { + code: Some(as_parens.expr.into_token_stream()), + conversion_strategy: None, + })) + } else { + Err(error) + } + } + } + } 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 + } +} + +fn is_default_attr(attr: &syn::Attribute) -> Result<bool, Error> { + let path = &attr.path; + if path.leading_colon.is_some() { + return Ok(false); + } + let segment = if let Some(segment) = single_value(path.segments.iter()) { + segment + } else { + return Ok(false); + }; + + if let syn::PathArguments::None = segment.arguments { + } else { + return Ok(false); + } + + Ok(segment.ident.to_string() == "default") +} + +fn parse_code_hack(meta: &syn::MetaList) -> Result<Option<TokenStream>, Error> { + for meta in meta.nested.iter() { + if let syn::NestedMeta::Meta(syn::Meta::NameValue(meta)) = meta { + if !meta.path.is_ident("_code") { + continue; + } + if let syn::Lit::Str(lit) = &meta.lit { + use std::str::FromStr; + return Ok(Some(TokenStream::from_str(&lit.value())?)); + } + }; + } + Ok(None) +} diff --git a/third_party/rust/smart-default/src/lib.rs b/third_party/rust/smart-default/src/lib.rs new file mode 100755 index 0000000000..650883226c --- /dev/null +++ b/third_party/rust/smart-default/src/lib.rs @@ -0,0 +1,84 @@ +extern crate proc_macro; +extern crate proc_macro2; +extern crate syn; + +extern crate quote; + +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 type - +/// 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 +/// +/// ``` +/// #[macro_use] +/// extern crate smart_default; +/// +/// # 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 100755 index 0000000000..452ac91f95 --- /dev/null +++ b/third_party/rust/smart-default/src/util.rs @@ -0,0 +1,29 @@ +use syn::spanned::Spanned; +use syn::parse::Error; + +/// 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) +} + +pub fn single_value<T>(mut it: impl Iterator<Item = T>) -> Option<T> { + if let Some(result) = it.next() { + if it.next().is_none() { + return Some(result) + } + } + None +} |