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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
//! # Azure Functions for Rust
//!
//! The Azure Functions for Rust crate supports writting Azure Functions in Rust.
//!
//! The following Azure Functions trigger bindings are supported:
//!
//! * [HTTP triggers](bindings/struct.HttpRequest.html)
//! * [Timer triggers](bindings/struct.TimerInfo.html)
//!
//! The following Azure Functions output bindings are supported:
//!
//! * [HTTP output](bindings/struct.HttpResponse.html)
//!
//! Eventually more bindings will be implemented, including custom binding data.
//!
//! # Examples
//!
//! Start by creating a new binary package:
//!
//! ```bash
//! $ cargo new --bin example
//! ```
//!
//! Edit `Cargo.toml` to include the following dependencies:
//!
//! ```toml
//! azure-functions = "0.1.3"
//! log = "0.4.2"
//! ```
//!
//! Azure Functions are implemented by applying a trigger attribute to a Rust function.
//!
//! For example, let's create `src/greet.rs` that implements a HTTP triggered function by
//! applying the `func` attribute:
//!
//! ```rust
//! # #![feature(proc_macro)] extern crate azure_functions;
//! # #[macro_use] extern crate log;
//! use azure_functions::func;
//! use azure_functions::bindings::{HttpRequest, HttpResponse};
//!
//! #[func]
//! #[binding(name = "request", auth_level = "anonymous")]
//! pub fn greet(request: &HttpRequest) -> HttpResponse {
//!     // Log the request on the Azure Functions Host
//!     info!("Request: {:?}", request);
//!
//!     // Return a formatted string as the response
//!     format!(
//!         "Hello from Rust, {}!",
//!         request.query_params().get("name").map_or("stranger", |x| x)
//!     ).into()
//! }
//! ```
//!
//! Replace the contents of `src/main.rs` with the following to register the function with
//! the Azure Functions Host:
//!
//! ```rust,ignore
//! #![feature(proc_macro)]
//!
//! #[macro_use]
//! extern crate log;
//! extern crate azure_functions;
//!
//! mod greet;
//!
//! // The main! macro generates an entrypoint for the binary
//! // Expects a list of Azure Functions to register with the Azure Functions host
//! azure_functions::main!{
//!     greet::greet
//! }
//! ```
//!
//! Run the application with the `--create <root>` option, where `<root>` is the path to
//! the desired Azure Functions application root directory:
//!
//! ```bash
//! $ export AzureWebJobsScriptRoot=path-to-root
//! $ cargo run -q -- --create $AzureWebJobsScriptRoot
//! ```
//!
//! Run the Azure Functions Host:
//!
//! ```bash
//! $ cd azure-functions-host/src/WebJobs.Script.WebHost
//! $ dotnet run
//! ```
//!
//! The above Azure Function can be invoked with `http://localhost:5000/api/greet?name=John`.
//!
//! The expected response would be `Hello from Rust, John!`.
#![feature(proc_macro)]
#![feature(proc_macro_mod)]
#![feature(proc_macro_gen)]
#![deny(missing_docs)]

extern crate azure_functions_codegen;
extern crate clap;
extern crate futures;
extern crate grpcio;
#[macro_use]
extern crate log;
extern crate protobuf;
extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
extern crate chrono;
extern crate tokio_threadpool;

#[doc(no_inline)]
pub use azure_functions_codegen::func;

mod cli;
mod context;
mod logger;
mod registry;

pub mod bindings;
#[doc(hidden)]
pub mod codegen;
pub mod http;
#[doc(hidden)]
pub mod rpc;
#[doc(no_inline)]
pub use azure_functions_codegen::main;
pub use context::Context;

use futures::Future;
use registry::Registry;
use std::sync::{Arc, Mutex};

#[doc(hidden)]
pub fn worker_main(args: impl Iterator<Item = String>, functions: &[&'static codegen::Function]) {
    let matches = cli::create_app().get_matches_from(args);
    let registry = Arc::new(Mutex::new(Registry::new(functions)));

    if let Some(root) = matches.value_of("create") {
        cli::generate_functions_app(root, registry);
        return;
    }

    let client = rpc::Client::new(
        matches
            .value_of("worker_id")
            .expect("A worker id is required.")
            .to_owned(),
        matches
            .value_of("max_message_length")
            .map(|len| len.parse::<i32>().expect("Invalid maximum message length")),
    );

    let host = matches.value_of("host").expect("A host is required.");
    let port = matches
        .value_of("port")
        .map(|port| port.parse::<u32>().expect("Invalid port number"))
        .expect("Port number is required.");

    println!("Connecting to Azure Functions host at {}:{}.", host, port);

    client
        .connect(host, port)
        .and_then(|client| {
            println!(
                "Connected to Azure Functions host version {}.",
                client.host_version().unwrap()
            );

            client.process_all_messages(registry)
        })
        .wait()
        .unwrap();
}