diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 02:49:50 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 02:49:50 +0000 |
commit | 9835e2ae736235810b4ea1c162ca5e65c547e770 (patch) | |
tree | 3fcebf40ed70e581d776a8a4c65923e8ec20e026 /vendor/varisat-internal-macros/src | |
parent | Releasing progress-linux version 1.70.0+dfsg2-1~progress7.99u1. (diff) | |
download | rustc-9835e2ae736235810b4ea1c162ca5e65c547e770.tar.xz rustc-9835e2ae736235810b4ea1c162ca5e65c547e770.zip |
Merging upstream version 1.71.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/varisat-internal-macros/src')
-rw-r--r-- | vendor/varisat-internal-macros/src/lib.rs | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/vendor/varisat-internal-macros/src/lib.rs b/vendor/varisat-internal-macros/src/lib.rs new file mode 100644 index 000000000..f6bfc8f25 --- /dev/null +++ b/vendor/varisat-internal-macros/src/lib.rs @@ -0,0 +1,216 @@ +//! Internal macros for the Varisat SAT solver. +#![recursion_limit = "128"] +use std::fmt::Write; + +use proc_macro2::TokenStream; +use quote::quote; +use syn::{ + parse_quote, punctuated::Punctuated, Attribute, Fields, Ident, Lit, LitStr, Meta, + MetaNameValue, Token, +}; +use synstructure::decl_derive; + +/// Get the doc comment as LitStr from the attributes +fn doc_from_attrs(attrs: &[Attribute]) -> Vec<LitStr> { + let mut lines = vec![]; + + for attr in attrs.iter() { + if let Ok(Meta::NameValue(MetaNameValue { + path, + lit: Lit::Str(doc_str), + .. + })) = attr.parse_meta() + { + if let Some(ident) = path.get_ident() { + if ident == "doc" { + lines.push(doc_str); + } + } + } + } + + lines +} + +/// Find a field inside the doc comment +fn get_doc_field(name: &str, attrs: &[Attribute]) -> Option<LitStr> { + let re = regex::Regex::new(&format!(r"\[{}: (.+?)\]( |$)", regex::escape(name))).unwrap(); + + for doc_str in doc_from_attrs(attrs) { + if let Some(expr_str) = re.captures(&doc_str.value()) { + let expr_str = expr_str.get(1).unwrap().as_str(); + let expr_str = LitStr::new(expr_str, doc_str.span()); + return Some(expr_str); + } + } + + None +} + +/// Derives a default instance from the documentation. +fn derive_doc_default(s: synstructure::Structure) -> TokenStream { + let variant = match s.variants() { + [variant] => variant, + _ => panic!("DocDefault requires a struct"), + }; + + let body = variant.construct(|field, _| { + get_doc_field("default", &field.attrs) + .map(|expr_str| { + expr_str + .parse::<TokenStream>() + .expect("error parsing default expression") + }) + .unwrap_or_else(|| parse_quote!(Default::default())) + }); + + s.gen_impl(quote! { + gen impl Default for @Self { + fn default() -> Self { + #body + } + } + }) +} + +decl_derive!([DocDefault] => derive_doc_default); + +/// Derives an update struct and method for a config struct. +fn derive_config_update(s: synstructure::Structure) -> TokenStream { + let variant = match s.variants() { + [variant] => variant, + _ => panic!("ConfigUpdate requires a struct"), + }; + + let fields = match variant.ast().fields { + Fields::Named(fields_named) => &fields_named.named, + _ => panic!("ConfigUpdate requires named fields"), + }; + + assert!( + s.referenced_ty_params().is_empty(), + "ConfigUpdate doesn't support type parameters" + ); + + let ident = &s.ast().ident; + let update_struct_ident = Ident::new(&format!("{}Update", ident), ident.span()); + + let vis = &s.ast().vis; + + let update_struct_body = fields + .iter() + .map(|field| { + let ty = &field.ty; + let mut field = field.clone(); + field.ty = parse_quote!(Option<#ty>); + field + }) + .collect::<Punctuated<_, Token![,]>>(); + + let check_ranges = fields + .iter() + .map(|field| { + if let Some(range) = get_doc_field("range", &field.attrs) { + // TODO use toml instead of fmt::Debug for errors? + let ident = &field.ident; + let error_msg = format!( + "{} must be in range {} but was set to {{:?}}", + quote!(#ident), + range.value() + ); + let range = range + .parse::<TokenStream>() + .expect("error parsing range expression"); + quote! { + if let Some(value) = &self.#ident { + anyhow::ensure!((#range).contains(value), #error_msg, value); + } + } + } else { + quote!() + } + }) + .collect::<TokenStream>(); + + let apply_updates = fields + .iter() + .map(|field| { + let ident = &field.ident; + quote! { + if let Some(value) = &self.#ident { + config.#ident = value.clone(); + } + } + }) + .collect::<TokenStream>(); + + let merge_updates = fields + .iter() + .map(|field| { + let ident = &field.ident; + quote! { + if let Some(value) = config_update.#ident { + self.#ident = Some(value); + } + } + }) + .collect::<TokenStream>(); + + let mut help_str = String::new(); + + for field in fields.iter() { + let ident = &field.ident; + writeln!(&mut help_str, "{}:", quote!(#ident)).unwrap(); + for line in doc_from_attrs(&field.attrs).iter() { + if line.value().is_empty() { + writeln!(&mut help_str).unwrap(); + } else { + writeln!(&mut help_str, " {}", line.value()).unwrap(); + } + } + writeln!(&mut help_str).unwrap(); + } + + let doc = format!("Updates configuration values of [`{}`].", ident); + + quote! { + #[doc = #doc] + #[derive(Default, serde::Serialize, serde::Deserialize)] + #[serde(deny_unknown_fields)] + #vis struct #update_struct_ident { + #update_struct_body + } + + impl #ident { + /// Return a string describing all supported configuration options. + pub fn help() -> &'static str { + #help_str + } + } + + impl #update_struct_ident { + /// Create an empty config update. + pub fn new() -> #update_struct_ident { + #update_struct_ident::default() + } + + /// Apply the configuration update. + /// + /// If an error occurs, the configuration is not changed. + pub fn apply(&self, config: &mut #ident) -> Result<(), anyhow::Error> { + #check_ranges + #apply_updates + Ok(()) + } + + /// Merge two configuration updates. + /// + /// Add the given update, overwriting values of the receiving update. + pub fn merge(&mut self, config_update: #update_struct_ident) { + #merge_updates + } + } + } +} + +decl_derive!([ConfigUpdate] => derive_config_update); |