azure-functions-codegen 0.4.0

Azure Functions for Rust code generation support
Documentation
use crate::util::{
    to_camel_case, AttributeArguments, MacroError, QuotableBorrowedStr, QuotableOption, TryFrom,
};
use azure_functions_shared::codegen;
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use std::borrow::Cow;
use syn::spanned::Spanned;
use syn::Lit;

pub struct HttpTrigger<'a>(pub Cow<'a, codegen::bindings::HttpTrigger>);

impl TryFrom<AttributeArguments> for HttpTrigger<'_> {
    type Error = MacroError;

    fn try_from(args: AttributeArguments) -> Result<Self, Self::Error> {
        let mut name = None;
        let mut auth_level = None;
        let mut methods = Vec::new();
        let mut route = None;
        let mut web_hook_type = None;

        for (key, value) in args.list.iter() {
            let key_str = key.to_string();

            match key_str.as_str() {
                "name" => match value {
                    Lit::Str(s) => {
                        name = Some(Cow::Owned(to_camel_case(&s.value())));
                    }
                    _ => {
                        return Err((
                            value.span(),
                            "expected a literal string value for the 'name' argument",
                        )
                            .into());
                    }
                },
                "auth_level" => match value {
                    Lit::Str(s) => {
                        let v = s.value();
                        auth_level = match v.as_str() {
                            "anonymous" | "function" | "admin" => Some(Cow::Owned(v)),
                            _ => {
                                return Err((value
                                    .span(),
                                    "expected 'anonymous', 'function', or 'admin' for the 'auth_level' attribute argument").into());
                            }
                        };
                    }
                    _ => {
                        return Err((
                            value.span(),
                            "expected a literal string value for the 'auth_level' argument",
                        )
                            .into());
                    }
                },
                "methods" => match value {
                    Lit::Str(s) => {
                        let mut invalid = Vec::new();
                        methods = s
                            .value()
                            .split(',')
                            .filter_map(|x| {
                                let x = x.trim();
                                match x {
                                    "get" | "post" | "delete" | "head" | "patch" | "put"
                                    | "options" | "trace" => Some(Cow::Owned(x.to_string())),
                                    _ => {
                                        invalid.push(x.to_string());
                                        None
                                    }
                                }
                            })
                            .collect();

                        if !invalid.is_empty() {
                            return Err((
                                value.span(),
                                format!("unsupported HTTP methods: {}", invalid.join(", "))
                                    .as_ref(),
                            )
                                .into());
                        }
                    }
                    _ => {
                        return Err((value
                                .span(),
                                "expected a comma-delimited literal string value for the 'methods' argument").into());
                    }
                },
                "route" => match value {
                    Lit::Str(s) => route = Some(Cow::Owned(s.value())),
                    _ => {
                        return Err((
                            value.span(),
                            "expected a literal string value for the 'route' argument",
                        )
                            .into());
                    }
                },
                "web_hook_type" => match value {
                    Lit::Str(s) => {
                        let s = s.value();
                        web_hook_type = match s.trim() {
                            "generic" | "github" | "slack" => Some(Cow::Owned(s)),
                            _ => {
                                return Err((value
                                    .span(),
                                    "expected 'generic', 'github', or 'slack' for the 'web_hook_type' attribute argument").into());
                            }
                        };
                    }
                    _ => {
                        return Err((
                            value.span(),
                            "expected a literal string value for the 'web_hook_type' argument",
                        )
                            .into());
                    }
                },
                _ => {
                    return Err((
                        key.span(),
                        format!("unsupported binding attribute argument '{}'", key_str).as_ref(),
                    )
                        .into());
                }
            };
        }

        Ok(HttpTrigger(Cow::Owned(codegen::bindings::HttpTrigger {
            name: name.expect("expected a name for a http trigger binding"),
            auth_level,
            methods: Cow::Owned(methods),
            route,
            web_hook_type,
        })))
    }
}

impl ToTokens for HttpTrigger<'_> {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let name = QuotableBorrowedStr(&self.0.name);
        let auth_level = QuotableOption(self.0.auth_level.as_ref().map(|x| QuotableBorrowedStr(x)));
        let methods = self.0.methods.iter().map(|x| QuotableBorrowedStr(x));
        let route = QuotableOption(self.0.route.as_ref().map(|x| QuotableBorrowedStr(x)));
        let web_hook_type = QuotableOption(
            self.0
                .web_hook_type
                .as_ref()
                .map(|x| QuotableBorrowedStr(x)),
        );

        quote!(::azure_functions::codegen::bindings::HttpTrigger {
            name: #name,
            auth_level: #auth_level,
            methods: ::std::borrow::Cow::Borrowed(&[#(#methods),*]),
            route: #route,
            web_hook_type: #web_hook_type,
        })
        .to_tokens(tokens)
    }
}