//! 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 { 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 { 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::() .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::>(); 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::() .expect("error parsing range expression"); quote! { if let Some(value) = &self.#ident { anyhow::ensure!((#range).contains(value), #error_msg, value); } } } else { quote!() } }) .collect::(); let apply_updates = fields .iter() .map(|field| { let ident = &field.ident; quote! { if let Some(value) = &self.#ident { config.#ident = value.clone(); } } }) .collect::(); let merge_updates = fields .iter() .map(|field| { let ident = &field.ident; quote! { if let Some(value) = config_update.#ident { self.#ident = Some(value); } } }) .collect::(); 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);