Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/query-group-macro/src/lib.rs')
| -rw-r--r-- | crates/query-group-macro/src/lib.rs | 71 |
1 files changed, 67 insertions, 4 deletions
diff --git a/crates/query-group-macro/src/lib.rs b/crates/query-group-macro/src/lib.rs index 2e2a24908e..3ade12733a 100644 --- a/crates/query-group-macro/src/lib.rs +++ b/crates/query-group-macro/src/lib.rs @@ -10,10 +10,13 @@ use queries::{ Queries, SetterKind, TrackedQuery, Transparent, }; use quote::{ToTokens, format_ident, quote}; +use syn::parse::{Parse, ParseStream}; +use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::visit_mut::VisitMut; use syn::{ - Attribute, FnArg, ItemTrait, Path, TraitItem, TraitItemFn, parse_quote, parse_quote_spanned, + Attribute, FnArg, ItemTrait, Path, Token, TraitItem, TraitItemFn, parse_quote, + parse_quote_spanned, }; mod queries; @@ -106,6 +109,66 @@ enum QueryKind { Interned, } +#[derive(Default, Debug, Clone)] +struct Cycle { + cycle_fn: Option<(syn::Ident, Path)>, + cycle_initial: Option<(syn::Ident, Path)>, + cycle_result: Option<(syn::Ident, Path)>, +} + +impl Parse for Cycle { + fn parse(input: ParseStream<'_>) -> syn::Result<Self> { + let options = Punctuated::<Option, Token![,]>::parse_terminated(input)?; + let mut cycle_fn = None; + let mut cycle_initial = None; + let mut cycle_result = None; + for option in options { + let name = option.name.to_string(); + match &*name { + "cycle_fn" => { + if cycle_fn.is_some() { + return Err(syn::Error::new_spanned(&option.name, "duplicate option")); + } + cycle_fn = Some((option.name, option.value)); + } + "cycle_initial" => { + if cycle_initial.is_some() { + return Err(syn::Error::new_spanned(&option.name, "duplicate option")); + } + cycle_initial = Some((option.name, option.value)); + } + "cycle_result" => { + if cycle_result.is_some() { + return Err(syn::Error::new_spanned(&option.name, "duplicate option")); + } + cycle_result = Some((option.name, option.value)); + } + _ => { + return Err(syn::Error::new_spanned( + &option.name, + "unknown cycle option. Accepted values: `cycle_result`, `cycle_fn`, `cycle_initial`", + )); + } + } + } + return Ok(Self { cycle_fn, cycle_initial, cycle_result }); + + struct Option { + name: syn::Ident, + value: Path, + } + + impl Parse for Option { + fn parse(input: ParseStream) -> syn::Result<Self> { + let name = input.parse()?; + input.parse::<Token![=]>()?; + let value = input.parse()?; + Ok(Self { name, value }) + } + } + } +} + pub(crate) fn query_group_impl( _args: proc_macro::TokenStream, input: proc_macro::TokenStream, @@ -155,8 +218,8 @@ pub(crate) fn query_group_impl( for SalsaAttr { name, tts, span } in salsa_attrs { match name.as_str() { "cycle" => { - let path = syn::parse::<Parenthesized<Path>>(tts)?; - cycle = Some(path.0.clone()) + let c = syn::parse::<Parenthesized<Cycle>>(tts)?; + cycle = Some(c.0); } "input" => { if !pat_and_tys.is_empty() { @@ -415,7 +478,7 @@ impl<T> syn::parse::Parse for Parenthesized<T> where T: syn::parse::Parse, { - fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> { + fn parse(input: ParseStream<'_>) -> syn::Result<Self> { let content; syn::parenthesized!(content in input); content.parse::<T>().map(Parenthesized) |