a3s-boot-macros 0.1.2

Attribute macros for a3s-boot
Documentation
use quote::quote;
use syn::{Attribute, Expr};

pub(crate) fn take_controller_pipeline_attrs(
    attrs: &[Attribute],
) -> (Vec<Attribute>, ControllerPipelineAttrs, Vec<syn::Error>) {
    let mut clean_attrs = Vec::new();
    let mut pipeline = ControllerPipelineAttrs::default();
    let mut errors = Vec::new();

    for attr in attrs {
        let Some(kind) = PipelineAttrKind::from_attribute(attr) else {
            clean_attrs.push(attr.clone());
            continue;
        };

        match attr.parse_args::<Expr>() {
            Ok(expr) => pipeline.specs.push(PipelineSpec { kind, expr }),
            Err(error) => errors.push(error),
        }
    }

    (clean_attrs, pipeline, errors)
}

pub(crate) fn take_route_pipeline_attrs(
    attrs: &[Attribute],
) -> (Vec<Attribute>, Vec<PipelineSpec>, Vec<syn::Error>) {
    let mut clean_attrs = Vec::new();
    let mut specs = Vec::new();
    let mut errors = Vec::new();

    for attr in attrs {
        let Some(kind) = PipelineAttrKind::from_attribute(attr) else {
            clean_attrs.push(attr.clone());
            continue;
        };

        match attr.parse_args::<Expr>() {
            Ok(expr) => specs.push(PipelineSpec { kind, expr }),
            Err(error) => errors.push(error),
        }
    }

    (clean_attrs, specs, errors)
}

#[derive(Default)]
pub(crate) struct ControllerPipelineAttrs {
    specs: Vec<PipelineSpec>,
}

impl ControllerPipelineAttrs {
    pub(crate) fn tokens(&self) -> Vec<proc_macro2::TokenStream> {
        self.specs.iter().map(PipelineSpec::token).collect()
    }
}

#[derive(Clone)]
pub(crate) struct PipelineSpec {
    kind: PipelineAttrKind,
    expr: Expr,
}

impl PipelineSpec {
    pub(crate) fn token(&self) -> proc_macro2::TokenStream {
        let expr = &self.expr;
        match self.kind {
            PipelineAttrKind::Guard => quote!(with_guard(#expr)),
            PipelineAttrKind::Interceptor => quote!(with_interceptor(#expr)),
            PipelineAttrKind::Filter => quote!(with_filter(#expr)),
            PipelineAttrKind::Pipe => quote!(with_pipe(#expr)),
        }
    }
}

#[derive(Clone, Copy)]
enum PipelineAttrKind {
    Guard,
    Interceptor,
    Filter,
    Pipe,
}

impl PipelineAttrKind {
    fn from_attribute(attr: &Attribute) -> Option<Self> {
        let ident = attr.path().segments.last()?.ident.to_string();
        match ident.as_str() {
            "use_guard" => Some(Self::Guard),
            "use_interceptor" => Some(Self::Interceptor),
            "use_filter" => Some(Self::Filter),
            "use_pipe" => Some(Self::Pipe),
            _ => None,
        }
    }
}