summaryrefslogtreecommitdiffstats
path: root/third_party/rust/smart-default/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/smart-default/src
parentInitial commit. (diff)
downloadfirefox-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-xthird_party/rust/smart-default/src/body_impl.rs130
-rwxr-xr-xthird_party/rust/smart-default/src/default_attr.rs125
-rwxr-xr-xthird_party/rust/smart-default/src/lib.rs84
-rwxr-xr-xthird_party/rust/smart-default/src/util.rs29
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
+}