blob: f6bfc8f25a9c80b1ba5f8ce93dbdb1fbd09b3f84 [file] [log] [blame] [edit]
//! 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);