graphrecords-macros 0.5.0

High-performance graph-based data records
Documentation
use super::optimizer::plan::PlanModel;
use crate::attribute::FromAttributes;
use proc_macro2::TokenStream;
use quote::quote;
use syn::{DeriveInput, Error, Ident, Result, meta::ParseNestedMeta, parse_quote};

#[derive(Default)]
struct OperationAttributes {
    scope: Option<Ident>,
}

impl FromAttributes for OperationAttributes {
    const NAMESPACE: &'static str = "operation";

    fn parse_meta(&mut self, meta: ParseNestedMeta) -> Result<()> {
        if !meta.path.is_ident("scope") {
            return Err(meta.error("unknown operation attribute"));
        }

        if self.scope.is_some() {
            return Err(meta.error("duplicate operation scope"));
        }

        let scope = meta.value()?.parse::<Ident>()?;
        if !matches!(scope.to_string().as_str(), "Element" | "Lane" | "Group") {
            return Err(Error::new(
                scope.span(),
                "operation scope must be `Element`, `Lane`, or `Group`",
            ));
        }

        self.scope = Some(scope);
        Ok(())
    }
}

pub fn expand(input: &DeriveInput) -> Result<TokenStream> {
    let PlanModel {
        ident,
        mut generics,
        crate_path,
        ..
    } = PlanModel::parse(input)?;
    let scope = OperationAttributes::from_attributes(&input.attrs)?
        .scope
        .ok_or_else(|| {
            Error::new_spanned(
                input,
                "missing `#[operation(scope = ...)]`, expected `Element`, `Lane`, or `Group`",
            )
        })?;

    let bound = quote! {
        #crate_path::operations::Prepare
            + #crate_path::optimizer::OperationInputs
            + #crate_path::Explain
    };

    generics
        .make_where_clause()
        .predicates
        .push(parse_quote!(Self: #bound));

    let (impl_generics, type_generics, where_clause) = generics.split_for_impl();

    Ok(quote! {
        #[automatically_derived]
        impl #impl_generics #crate_path::operations::Operation
            for #ident #type_generics #where_clause
        {
            type Scope = #crate_path::operations::#scope;
        }
    })
}