1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#![feature(proc_macro)]
#![feature(drain_filter)]
#![feature(try_from)]
#![recursion_limit = "128"]

extern crate proc_macro;
extern crate proc_macro2;
#[macro_use]
extern crate syn;
#[macro_use]
extern crate quote;
#[macro_use]
extern crate lazy_static;

mod bindings;
mod func;
mod util;

use func::func_attr_impl;
use proc_macro::TokenStream;
use std::convert::TryFrom;
use std::fmt::Write;
use std::path::Path;
use util::PathVec;

/// Implements the `main!` macro.
///
/// The `main!` macro is used to register a list of Azure Functions with
/// the Azure Functions host.
///
/// This macro expects a comma-separated list of functions that have the
/// #[func] attribute applied.
///
/// # Examples
///
/// ```rust,ignore
/// azure_functions::main!{
///     module::my_azure_function
/// }
/// ```
#[proc_macro]
pub fn main(input: TokenStream) -> TokenStream {
    let funcs = match PathVec::try_from(input.clone()) {
        Ok(funcs) => funcs,
        Err(e) => {
            e.emit();
            return input;
        }
    };
    let funcs: Vec<syn::Expr> = funcs
        .into_iter()
        .map(|path| {
            let mut expr = String::new();
            if path.leading_colon.is_some() {
                expr += "::";
            }

            let mut segments = path.segments.into_iter().peekable();
            while let Some(segment) = segments.next() {
                if segments.peek().is_some() {
                    write!(&mut expr, "{}::", segment.ident).unwrap();
                } else {
                    write!(
                        &mut expr,
                        "__{}_FUNCTION",
                        segment.ident.to_string().to_uppercase()
                    ).unwrap();
                }
            }

            syn::parse_str::<syn::Expr>(&expr).unwrap()
        })
        .collect();

    let expanded = quote!{
        pub fn main() {
            azure_functions::worker_main(
                std::env::args(),
                &[#(&#funcs),*],
            );
        }
    };

    expanded.into()
}

/// Implements the `func` attribute.
///
/// This attribute is used to turn a Rust function into an Azure Function.
///
/// # Examples
///
/// ```rust,ignore
/// use azure_functions::func;
/// use azure_functions::bindings::HttpRequest;
///
/// #[func]
/// pub fn example(req: &HttpRequest) {
/// }
#[proc_macro_attribute]
pub fn func(args: TokenStream, input: TokenStream) -> TokenStream {
    func_attr_impl(args, input)
}

/// This exists to enable us to create a module from generated code.
/// A proc_macro solution to https://github.com/rust-lang/rfcs/issues/752
#[doc(hidden)]
#[proc_macro_attribute]
pub fn generated_mod(_: TokenStream, input: TokenStream) -> TokenStream {
    let m = syn::parse::<syn::ItemMod>(input.clone()).unwrap();

    let ident = &m.ident;

    let mut path = Path::new(&std::env::var("OUT_DIR").unwrap()).join(ident.to_string());

    path.set_extension("rs");

    let path = path.to_str().unwrap().to_string();

    quote!(
        #[path = #path]
        mod #ident;
    ).into()
}