nest-rs-http-macros 0.1.0

Internal — HTTP decorator macros (#[controller], #[routes], verb attributes, #[interceptor]); re-exported by nest-rs-http, not a direct dependency.
Documentation
//! `syn` attribute-parsing helpers shared by the HTTP decorators.

use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::punctuated::Punctuated;
use syn::{Attribute, Expr, Lit, LitStr, Path, Token};

pub(crate) fn expr_str(expr: &Expr) -> syn::Result<LitStr> {
    match expr {
        Expr::Lit(syn::ExprLit {
            lit: Lit::Str(s), ..
        }) => Ok(s.clone()),
        other => Err(syn::Error::new_spanned(other, "expected a string literal")),
    }
}

pub(crate) fn opt_str(value: &Option<LitStr>) -> TokenStream2 {
    match value {
        Some(lit) => quote! { ::core::option::Option::Some(#lit) },
        None => quote! { ::core::option::Option::None },
    }
}

/// Extract and remove a flag attribute (no args, no parens) like `#[public]`.
/// Returns `true` when present (and removes it), `false` when absent.
pub(crate) fn take_flag_attr(attrs: &mut Vec<Attribute>, ident: &str) -> bool {
    let Some(pos) = attrs.iter().position(|a| a.path().is_ident(ident)) else {
        return false;
    };
    attrs.remove(pos);
    true
}

/// Extract and remove a `#[<ident>(PathA, PathB)]` attribute (empty when
/// absent); the attribute is consumed so it never reaches the compiler as
/// unknown. At most one accepted; a second of the same ident is rejected with
/// a clear message.
pub(crate) fn take_use_attr(attrs: &mut Vec<Attribute>, ident: &str) -> syn::Result<Vec<Path>> {
    let Some(pos) = attrs.iter().position(|a| a.path().is_ident(ident)) else {
        return Ok(Vec::new());
    };
    let attr = attrs.remove(pos);
    if attrs.iter().any(|a| a.path().is_ident(ident)) {
        return Err(syn::Error::new_spanned(
            &attr,
            format!("at most one `#[{ident}(...)]` is allowed; list every entry in it"),
        ));
    }
    Ok(attr
        .parse_args_with(Punctuated::<Path, Token![,]>::parse_terminated)?
        .into_iter()
        .collect())
}