├── .all-contributorsrc ├── .clabot ├── .dockerignore ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── main.yml ├── .gitignore ├── .gitmodules ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── azure-functions-codegen ├── Cargo.toml └── src │ ├── export.rs │ ├── func.rs │ ├── func │ ├── invoker.rs │ └── output_bindings.rs │ └── lib.rs ├── azure-functions-durable ├── Cargo.toml └── src │ ├── client.rs │ ├── endpoint.rs │ ├── error.rs │ └── lib.rs ├── azure-functions-sdk ├── Cargo.toml └── src │ ├── commands.rs │ ├── commands │ ├── new.rs │ ├── new │ │ ├── activity.rs │ │ ├── blob.rs │ │ ├── cosmos_db.rs │ │ ├── event_grid.rs │ │ ├── event_hub.rs │ │ ├── http.rs │ │ ├── orchestration.rs │ │ ├── queue.rs │ │ ├── service_bus.rs │ │ └── timer.rs │ ├── new_app.rs │ └── run.rs │ ├── main.rs │ ├── templates │ ├── new-app │ │ ├── Dockerfile.hbs │ │ ├── dockerignore.hbs │ │ ├── functions_mod.rs.hbs │ │ ├── host.json.hbs │ │ ├── launch.json.hbs │ │ ├── local.settings.json.hbs │ │ ├── main.rs.hbs │ │ └── tasks.json.hbs │ └── new │ │ ├── activity.rs.hbs │ │ ├── blob.rs.hbs │ │ ├── cosmosdb.rs.hbs │ │ ├── eventgrid.rs.hbs │ │ ├── eventhub.rs.hbs │ │ ├── http.rs.hbs │ │ ├── orchestration.rs.hbs │ │ ├── queue.rs.hbs │ │ ├── servicebus.rs.hbs │ │ └── timer.rs.hbs │ └── util.rs ├── azure-functions-shared-codegen ├── Cargo.toml └── src │ ├── binding.rs │ └── lib.rs ├── azure-functions-shared ├── Cargo.toml ├── build.rs ├── cache │ ├── README.md │ └── azure_functions_rpc_messages.rs └── src │ ├── codegen.rs │ ├── codegen │ ├── bindings.rs │ ├── bindings │ │ ├── activity_trigger.rs │ │ ├── blob.rs │ │ ├── blob_trigger.rs │ │ ├── cosmos_db.rs │ │ ├── cosmos_db_trigger.rs │ │ ├── durable_client.rs │ │ ├── event_grid_trigger.rs │ │ ├── event_hub.rs │ │ ├── event_hub_trigger.rs │ │ ├── generic.rs │ │ ├── http.rs │ │ ├── http_trigger.rs │ │ ├── orchestration_trigger.rs │ │ ├── queue.rs │ │ ├── queue_trigger.rs │ │ ├── send_grid.rs │ │ ├── service_bus.rs │ │ ├── service_bus_trigger.rs │ │ ├── signalr.rs │ │ ├── signalr_connection_info.rs │ │ ├── table.rs │ │ ├── timer_trigger.rs │ │ └── twilio_sms.rs │ ├── function.rs │ ├── quotable.rs │ └── value.rs │ ├── lib.rs │ └── util.rs ├── azure-functions ├── Cargo.toml └── src │ ├── backtrace.rs │ ├── bindings.rs │ ├── bindings │ ├── blob.rs │ ├── blob_trigger.rs │ ├── cosmos_db_document.rs │ ├── cosmos_db_trigger.rs │ ├── durable_activity_context.rs │ ├── durable_orchestration_client.rs │ ├── durable_orchestration_context.rs │ ├── event_grid_event.rs │ ├── event_hub_message.rs │ ├── event_hub_trigger.rs │ ├── generic_input.rs │ ├── generic_output.rs │ ├── generic_trigger.rs │ ├── http_request.rs │ ├── http_response.rs │ ├── queue_message.rs │ ├── queue_trigger.rs │ ├── send_grid_message.rs │ ├── service_bus_message.rs │ ├── service_bus_trigger.rs │ ├── signalr_connection_info.rs │ ├── signalr_group_action.rs │ ├── signalr_message.rs │ ├── table.rs │ ├── timer_info.rs │ └── twilio_sms_message.rs │ ├── blob.rs │ ├── blob │ └── properties.rs │ ├── commands.rs │ ├── commands │ ├── init.rs │ ├── run.rs │ └── sync_extensions.rs │ ├── context.rs │ ├── durable.rs │ ├── durable │ ├── action_future.rs │ ├── actions.rs │ ├── activity_output.rs │ ├── history.rs │ ├── join_all.rs │ ├── orchestration_output.rs │ ├── orchestration_state.rs │ └── select_all.rs │ ├── event_hub.rs │ ├── event_hub │ ├── partition_context.rs │ ├── runtime_information.rs │ └── system_properties.rs │ ├── generic.rs │ ├── http.rs │ ├── http │ ├── body.rs │ ├── response_builder.rs │ └── status.rs │ ├── lib.rs │ ├── logger.rs │ ├── registry.rs │ ├── send_grid.rs │ ├── send_grid │ ├── attachment.rs │ ├── bcc_settings.rs │ ├── bypass_list_management.rs │ ├── click_tracking.rs │ ├── content.rs │ ├── email_address.rs │ ├── footer_settings.rs │ ├── google_analytics.rs │ ├── mail_settings.rs │ ├── message_builder.rs │ ├── open_tracking.rs │ ├── personalization.rs │ ├── sandbox_mode.rs │ ├── spam_check.rs │ ├── subscription_tracking.rs │ ├── tracking_settings.rs │ └── unsubscribe_group.rs │ ├── signalr.rs │ ├── timer.rs │ ├── timer │ └── schedule_status.rs │ ├── util.rs │ └── worker.rs ├── docker └── build-image │ └── Dockerfile └── examples ├── blob ├── .dockerignore ├── .vscode │ ├── launch.json │ └── tasks.json ├── Cargo.toml ├── Dockerfile ├── README.md ├── host.json ├── local.settings.json └── src │ ├── functions │ ├── blob_watcher.rs │ ├── copy_blob.rs │ ├── create_blob.rs │ ├── mod.rs │ └── print_blob.rs │ └── main.rs ├── cosmosdb ├── .dockerignore ├── .vscode │ ├── launch.json │ └── tasks.json ├── Cargo.toml ├── Dockerfile ├── README.md ├── host.json ├── local.settings.json └── src │ ├── functions │ ├── create_document.rs │ ├── log_documents.rs │ ├── mod.rs │ ├── query_documents.rs │ └── read_document.rs │ └── main.rs ├── durable-functions ├── .dockerignore ├── .vscode │ ├── launch.json │ └── tasks.json ├── Cargo.toml ├── Dockerfile ├── README.md ├── host.json ├── local.settings.json └── src │ ├── functions │ ├── call_join.rs │ ├── join.rs │ ├── looping.rs │ ├── mod.rs │ ├── raise_event.rs │ ├── say_hello.rs │ ├── select.rs │ ├── start.rs │ ├── start_looping.rs │ ├── timer.rs │ └── wait_for_event.rs │ └── main.rs ├── event-grid ├── .dockerignore ├── .vscode │ ├── launch.json │ └── tasks.json ├── Cargo.toml ├── Dockerfile ├── README.md ├── host.json ├── local.settings.json └── src │ ├── functions │ ├── log_event.rs │ └── mod.rs │ └── main.rs ├── event-hub ├── .dockerignore ├── .vscode │ ├── launch.json │ └── tasks.json ├── Cargo.toml ├── Dockerfile ├── README.md ├── host.json ├── local.settings.json └── src │ ├── functions │ ├── create_event.rs │ ├── log_event.rs │ └── mod.rs │ └── main.rs ├── generic ├── .dockerignore ├── .vscode │ ├── launch.json │ └── tasks.json ├── Cargo.toml ├── Dockerfile ├── README.md ├── host.json ├── local.settings.json └── src │ ├── functions │ ├── create_document.rs │ ├── log_documents.rs │ ├── mod.rs │ ├── query_documents.rs │ └── read_document.rs │ └── main.rs ├── http ├── .dockerignore ├── .vscode │ ├── launch.json │ └── tasks.json ├── Cargo.toml ├── Dockerfile ├── README.md ├── host.json ├── local.settings.json └── src │ ├── functions │ ├── greet.rs │ ├── greet_async.rs │ ├── greet_with_json.rs │ └── mod.rs │ └── main.rs ├── queue ├── .dockerignore ├── .vscode │ ├── launch.json │ └── tasks.json ├── Cargo.toml ├── Dockerfile ├── README.md ├── host.json ├── local.settings.json └── src │ ├── functions │ ├── mod.rs │ ├── queue.rs │ └── queue_with_output.rs │ └── main.rs ├── sendgrid ├── .dockerignore ├── .vscode │ ├── launch.json │ └── tasks.json ├── Cargo.toml ├── Dockerfile ├── README.md ├── host.json ├── local.settings.json └── src │ ├── functions │ ├── mod.rs │ └── send_email.rs │ └── main.rs ├── service-bus ├── .dockerignore ├── .vscode │ ├── launch.json │ └── tasks.json ├── Cargo.toml ├── Dockerfile ├── README.md ├── host.json ├── local.settings.json └── src │ ├── functions │ ├── create_queue_message.rs │ ├── create_topic_message.rs │ ├── log_queue_message.rs │ ├── log_topic_message.rs │ └── mod.rs │ └── main.rs ├── signalr ├── .dockerignore ├── .vscode │ ├── launch.json │ └── tasks.json ├── Cargo.toml ├── Dockerfile ├── README.md ├── host.json ├── local.settings.json └── src │ ├── functions │ ├── add_to_group.rs │ ├── mod.rs │ ├── negotiate.rs │ ├── remove_from_group.rs │ └── send_message.rs │ ├── main.rs │ └── serialization.rs ├── table ├── .dockerignore ├── .vscode │ ├── launch.json │ └── tasks.json ├── Cargo.toml ├── Dockerfile ├── README.md ├── host.json ├── local.settings.json └── src │ ├── functions │ ├── create_row.rs │ ├── mod.rs │ └── read_row.rs │ └── main.rs ├── timer ├── .dockerignore ├── .vscode │ ├── launch.json │ └── tasks.json ├── Cargo.toml ├── Dockerfile ├── README.md ├── host.json ├── local.settings.json └── src │ ├── functions │ ├── mod.rs │ └── timer.rs │ └── main.rs └── twilio ├── .dockerignore ├── .vscode ├── launch.json └── tasks.json ├── Cargo.toml ├── Dockerfile ├── README.md ├── host.json ├── local.settings.json └── src ├── functions ├── mod.rs └── send_sms.rs └── main.rs /.clabot: -------------------------------------------------------------------------------- 1 | { 2 | "contributors": [ 3 | "peterhuene", 4 | "rylev", 5 | "dependabot-bot", 6 | "dependabot-preview[bot]", 7 | "allcontributors[bot]", 8 | "t-eckert", 9 | "rtyler", 10 | "dmolokanov", 11 | "slyons", 12 | "ajnirp", 13 | "cbrevik", 14 | "dbcfd", 15 | "demoray" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | target/ 2 | Cargo.lock 3 | .vscode/ 4 | .git/ -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | ## Steps to reproduce 8 | 9 | 10 | ## Expected behavior 11 | 12 | 13 | ## Actual behavior 14 | 15 | 16 | ## Environment information (OS, versions, etc.) 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## What is the goal of this pull request? 2 | 3 | ## What does this pull request change? 4 | 5 | ## What work remains to be done? 6 | 7 | ## Do you consider it adequately tested? 8 | 9 | YES | NO 10 | 11 | ## Related Issues 12 | 13 | ## Notes -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | **/target 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | .vscode/ 13 | 14 | # Ignore generated files by Vim 15 | *.sw* 16 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "azure-functions-shared/protobuf"] 2 | path = azure-functions-shared/protobuf 3 | url = https://github.com/peterhuene/azure-functions-language-worker-protobuf 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = [ 4 | "azure-functions", 5 | "azure-functions-codegen", 6 | "azure-functions-durable", 7 | "azure-functions-sdk", 8 | "azure-functions-shared", 9 | "azure-functions-shared-codegen", 10 | "examples/http", 11 | "examples/timer", 12 | "examples/queue", 13 | "examples/blob", 14 | "examples/table", 15 | "examples/event-grid", 16 | "examples/event-hub", 17 | "examples/cosmosdb", 18 | "examples/signalr", 19 | "examples/service-bus", 20 | "examples/twilio", 21 | "examples/sendgrid", 22 | "examples/generic", 23 | "examples/durable-functions", 24 | ] 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Peter Huene 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /azure-functions-codegen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "azure-functions-codegen" 3 | license = "MIT" 4 | version = "0.11.0" 5 | authors = ["Peter Huene "] 6 | description = "Azure Functions for Rust code generation support" 7 | repository = "https://github.com/peterhuene/azure-functions-rs/" 8 | homepage = "https://functions.rs" 9 | edition = "2018" 10 | 11 | [lib] 12 | proc-macro = true 13 | 14 | [dependencies] 15 | azure-functions-shared = { version = "0.11.0", path = "../azure-functions-shared" } 16 | quote = "1.0.2" 17 | syn = { version = "1.0.7", features = ["full"] } 18 | proc-macro2 = { version = "1.0.6" } 19 | 20 | [features] 21 | unstable = ["azure-functions-shared/unstable"] 22 | -------------------------------------------------------------------------------- /azure-functions-codegen/src/export.rs: -------------------------------------------------------------------------------- 1 | use azure_functions_shared::codegen::{last_segment_in_path, macro_panic}; 2 | use proc_macro::TokenStream; 3 | use proc_macro2::Span; 4 | use quote::quote; 5 | use syn::{ 6 | parse, 7 | parse::{Parse, ParseStream}, 8 | punctuated::Punctuated, 9 | spanned::Spanned, 10 | Ident, Path, Token, 11 | }; 12 | 13 | #[derive(Default)] 14 | struct PathVec(Vec); 15 | 16 | impl Parse for PathVec { 17 | fn parse(input: ParseStream) -> parse::Result { 18 | let paths = Punctuated::::parse_terminated(input)?; 19 | 20 | Ok(PathVec(paths.into_iter().collect())) 21 | } 22 | } 23 | 24 | impl IntoIterator for PathVec { 25 | type Item = Path; 26 | type IntoIter = std::vec::IntoIter; 27 | 28 | fn into_iter(self) -> Self::IntoIter { 29 | self.0.into_iter() 30 | } 31 | } 32 | 33 | impl From for PathVec { 34 | fn from(stream: TokenStream) -> Self { 35 | if stream.is_empty() { 36 | return Self::default(); 37 | } 38 | 39 | parse::(stream) 40 | .map_err(|e| macro_panic(Span::call_site(), e.to_string())) 41 | .unwrap() 42 | } 43 | } 44 | 45 | pub fn export_impl(input: TokenStream) -> TokenStream { 46 | let mut funcs = Vec::new(); 47 | for mut path in PathVec::from(input).into_iter() { 48 | let last = last_segment_in_path(&path); 49 | let identifier = Ident::new( 50 | &format!("{}_FUNCTION", last.ident.to_string().to_uppercase()), 51 | last.span(), 52 | ); 53 | 54 | path.segments.pop(); 55 | 56 | funcs.push(quote!(#path#identifier)); 57 | } 58 | 59 | quote!( 60 | pub const EXPORTS: &[&::azure_functions::codegen::Function] = &[#(&#funcs),*]; 61 | ) 62 | .into() 63 | } 64 | -------------------------------------------------------------------------------- /azure-functions-durable/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "azure-functions-durable" 3 | license = "MIT" 4 | version = "0.11.0" 5 | authors = ["Peter Huene "] 6 | description = "Durable Functions for Rust" 7 | repository = "https://github.com/peterhuene/azure-functions-rs/" 8 | homepage = "https://functions.rs" 9 | edition = "2018" 10 | 11 | [dependencies] 12 | futures-preview = "0.3.0-alpha.19" 13 | hyper = { version = "0.13.0-alpha.4", features = ["unstable-stream"] } 14 | serde = { version = "1.0.97", features = ["derive"] } 15 | serde_json = "1.0.40" 16 | tokio = "0.2.0-alpha.6" 17 | chrono = { version = "0.4.9", features = ["serde"] } 18 | url = "1.7.2" 19 | log = "0.4.7" 20 | 21 | [dev-dependencies] 22 | mockito = "0.19.0" 23 | -------------------------------------------------------------------------------- /azure-functions-durable/src/error.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | use std::fmt::{Display, Formatter}; 3 | 4 | /// Represents a Durable Functions HTTP client error. 5 | #[derive(Debug, Clone, PartialEq)] 6 | pub enum ClientError { 7 | /// Orchestration instance is in a failed or terminated state. 8 | InstanceFailedOrTerminated, 9 | /// Orchestration instance is in a completed or failed state. 10 | InstanceCompletedOrFailed, 11 | /// The orchestration instance was not found. 12 | InstanceNotFound, 13 | /// The request contained invalid JSON data. 14 | BadRequest, 15 | /// The specified orchestrator function doesn't exist or the request contained invalid JSON data. 16 | BadCreateRequest, 17 | /// The request failed due to an exception while processing the request. 18 | InternalServerError, 19 | /// The error is a message. 20 | Message(String), 21 | } 22 | 23 | impl Display for ClientError { 24 | fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { 25 | match self { 26 | Self::InstanceFailedOrTerminated => write!(f, "theinstance failed or was terminated"), 27 | Self::InstanceCompletedOrFailed => write!(f, "instance completed or failed"), 28 | Self::InstanceNotFound => { 29 | write!(f, "instance doesn't exist or has not started running") 30 | } 31 | Self::BadRequest => write!(f, "request content was not valid JSON"), 32 | Self::BadCreateRequest => write!(f, "the specified orchestrator function doesn't exist, the specified instance ID was not valid, or request content was not valid JSON"), 33 | Self::InternalServerError => write!(f, "internal server error"), 34 | Self::Message(msg) => write!(f, "{}", msg), 35 | } 36 | } 37 | } 38 | 39 | impl Error for ClientError { 40 | fn source(&self) -> Option<&(dyn Error + 'static)> { 41 | None 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /azure-functions-durable/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Durable Functions HTTP client for Rust. 2 | 3 | #![deny(missing_docs)] 4 | 5 | mod client; 6 | mod endpoint; 7 | mod error; 8 | 9 | pub use self::client::*; 10 | pub use self::endpoint::*; 11 | pub use self::error::*; 12 | 13 | /// The result type for the Durable Functions HTTP client. 14 | pub type Result = std::result::Result; 15 | -------------------------------------------------------------------------------- /azure-functions-sdk/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "azure-functions-sdk" 3 | license = "MIT" 4 | version = "0.11.0" 5 | authors = ["Peter Huene "] 6 | description = "Azure Functions for Rust Developer Tools" 7 | repository = "https://github.com/peterhuene/azure-functions-rs/" 8 | homepage = "https://functions.rs" 9 | edition = "2018" 10 | 11 | [[bin]] 12 | name = "cargo-func" 13 | path = "src/main.rs" 14 | 15 | [dependencies] 16 | colored = "1.8" 17 | clap = "2.33.0" 18 | atty = "0.2.13" 19 | handlebars = "2.0.2" 20 | serde_json = "1.0.41" 21 | tempdir = "0.3.7" 22 | regex = "1.3.1" 23 | syn = { version = "1.0.7", features = ["full", "extra-traits"] } 24 | lazy_static = "1.3.0" 25 | ctrlc = "3.1.3" 26 | -------------------------------------------------------------------------------- /azure-functions-sdk/src/commands.rs: -------------------------------------------------------------------------------- 1 | macro_rules! template { 2 | ( $templates:expr, $dir:expr, $file:expr ) => { 3 | $templates 4 | .register_template_string( 5 | $file, 6 | include_str!(concat!("templates/", $dir, "/", $file, ".hbs")), 7 | ) 8 | .expect(concat!( 9 | "failed to register ", 10 | $dir, 11 | "/", 12 | $file, 13 | " template." 14 | )); 15 | }; 16 | } 17 | 18 | lazy_static::lazy_static! { 19 | static ref TEMPLATES: handlebars::Handlebars = { 20 | let mut templates = handlebars::Handlebars::new(); 21 | 22 | template!(templates, "new-app", "local.settings.json"); 23 | template!(templates, "new-app", "host.json"); 24 | template!(templates, "new-app", "Dockerfile"); 25 | template!(templates, "new-app", "dockerignore"); 26 | template!(templates, "new-app", "functions_mod.rs"); 27 | template!(templates, "new-app", "launch.json"); 28 | template!(templates, "new-app", "main.rs"); 29 | template!(templates, "new-app", "tasks.json"); 30 | 31 | template!(templates, "new", "http.rs"); 32 | template!(templates, "new", "blob.rs"); 33 | template!(templates, "new", "queue.rs"); 34 | template!(templates, "new", "timer.rs"); 35 | template!(templates, "new", "eventgrid.rs"); 36 | template!(templates, "new", "eventhub.rs"); 37 | template!(templates, "new", "cosmosdb.rs"); 38 | template!(templates, "new", "servicebus.rs"); 39 | template!(templates, "new", "activity.rs"); 40 | template!(templates, "new", "orchestration.rs"); 41 | 42 | templates 43 | }; 44 | } 45 | 46 | mod new; 47 | mod new_app; 48 | mod run; 49 | 50 | pub use self::new::*; 51 | pub use self::new_app::*; 52 | pub use self::run::*; 53 | -------------------------------------------------------------------------------- /azure-functions-sdk/src/commands/new/activity.rs: -------------------------------------------------------------------------------- 1 | use crate::commands::new::create_function; 2 | use clap::{App, Arg, ArgMatches, SubCommand}; 3 | use serde_json::json; 4 | 5 | pub struct Activity<'a> { 6 | name: &'a str, 7 | } 8 | 9 | impl<'a> Activity<'a> { 10 | pub fn create_subcommand<'b>() -> App<'a, 'b> { 11 | SubCommand::with_name("activity") 12 | .about("Creates a new Activity Function for Durable Functions.") 13 | .arg( 14 | Arg::with_name("positional-name") 15 | .value_name("NAME") 16 | .help("The name of the new Azure Function. You may specify this as --name instead.") 17 | .conflicts_with("name") 18 | .required(true), 19 | ) 20 | .arg( 21 | Arg::with_name("name") 22 | .long("name") 23 | .short("n") 24 | .value_name("NAME") 25 | .help("The name of the new Azure Function. You may specify this as instead (i.e., without typing --name).") 26 | ) 27 | } 28 | 29 | pub fn execute(&self, quiet: bool) -> Result<(), String> { 30 | let data = json!({ 31 | "name": self.name, 32 | }); 33 | 34 | create_function(self.name, "activity.rs", &data, quiet) 35 | } 36 | } 37 | 38 | impl<'a> From<&'a ArgMatches<'a>> for Activity<'a> { 39 | fn from(args: &'a ArgMatches<'a>) -> Self { 40 | Activity { 41 | name: args 42 | .value_of("positional-name") 43 | .unwrap_or_else(|| args.value_of("name").expect("A NAME argument is needed")), 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /azure-functions-sdk/src/commands/new/blob.rs: -------------------------------------------------------------------------------- 1 | use crate::commands::new::create_function; 2 | use clap::{App, Arg, ArgMatches, SubCommand}; 3 | use serde_json::json; 4 | 5 | pub struct Blob<'a> { 6 | name: &'a str, 7 | path: &'a str, 8 | } 9 | 10 | impl<'a> Blob<'a> { 11 | pub fn create_subcommand<'b>() -> App<'a, 'b> { 12 | SubCommand::with_name("blob") 13 | .about("Creates a new blob triggered Azure Function.") 14 | .arg( 15 | Arg::with_name("positional-name") 16 | .value_name("NAME") 17 | .help("The name of the new Azure Function. You may specify this as --name instead.") 18 | .conflicts_with("name") 19 | .required(true), 20 | ) 21 | .arg( 22 | Arg::with_name("name") 23 | .long("name") 24 | .short("n") 25 | .value_name("NAME") 26 | .help("The name of the new Azure Function. You may specify this as instead (i.e., without typing --name).") 27 | ) 28 | .arg( 29 | Arg::with_name("path") 30 | .long("path") 31 | .short("p") 32 | .value_name("PATH") 33 | .help("The blob storage path to monitor.") 34 | .required(true), 35 | ) 36 | } 37 | 38 | pub fn execute(&self, quiet: bool) -> Result<(), String> { 39 | let data = json!({ 40 | "name": self.name, 41 | "path": self.path 42 | }); 43 | 44 | create_function(self.name, "blob.rs", &data, quiet) 45 | } 46 | } 47 | 48 | impl<'a> From<&'a ArgMatches<'a>> for Blob<'a> { 49 | fn from(args: &'a ArgMatches<'a>) -> Self { 50 | Blob { 51 | name: args 52 | .value_of("positional-name") 53 | .unwrap_or_else(|| args.value_of("name").expect("A NAME argument is needed")), 54 | path: args.value_of("path").unwrap(), 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /azure-functions-sdk/src/commands/new/event_grid.rs: -------------------------------------------------------------------------------- 1 | use crate::commands::new::create_function; 2 | use clap::{App, Arg, ArgMatches, SubCommand}; 3 | use serde_json::json; 4 | 5 | pub struct EventGrid<'a> { 6 | name: &'a str, 7 | } 8 | 9 | impl<'a> EventGrid<'a> { 10 | pub fn create_subcommand<'b>() -> App<'a, 'b> { 11 | SubCommand::with_name("event-grid") 12 | .about("Creates a new Event Grid triggered Azure Function.") 13 | .arg( 14 | Arg::with_name("positional-name") 15 | .value_name("NAME") 16 | .help("The name of the new Azure Function. You may specify this as --name instead.") 17 | .conflicts_with("name") 18 | .required(true), 19 | ) 20 | .arg( 21 | Arg::with_name("name") 22 | .long("name") 23 | .short("n") 24 | .value_name("NAME") 25 | .help("The name of the new Azure Function. You may specify this as instead (i.e., without typing --name).") 26 | ) 27 | } 28 | 29 | pub fn execute(&self, quiet: bool) -> Result<(), String> { 30 | let data = json!({ 31 | "name": self.name, 32 | }); 33 | 34 | create_function(self.name, "eventgrid.rs", &data, quiet) 35 | } 36 | } 37 | 38 | impl<'a> From<&'a ArgMatches<'a>> for EventGrid<'a> { 39 | fn from(args: &'a ArgMatches<'a>) -> Self { 40 | EventGrid { 41 | name: args 42 | .value_of("positional-name") 43 | .unwrap_or_else(|| args.value_of("name").expect("A NAME argument is needed")), 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /azure-functions-sdk/src/commands/new/event_hub.rs: -------------------------------------------------------------------------------- 1 | use crate::commands::new::create_function; 2 | use clap::{App, Arg, ArgMatches, SubCommand}; 3 | use serde_json::json; 4 | 5 | pub struct EventHub<'a> { 6 | name: &'a str, 7 | connection: &'a str, 8 | hub_name: &'a str, 9 | } 10 | 11 | impl<'a> EventHub<'a> { 12 | pub fn create_subcommand<'b>() -> App<'a, 'b> { 13 | SubCommand::with_name("event-hub") 14 | .about("Creates a new Event Hub triggered Azure Function.") 15 | .arg( 16 | Arg::with_name("positional-name") 17 | .value_name("NAME") 18 | .help("The name of the new Azure Function. You may specify this as --name instead.") 19 | .conflicts_with("name") 20 | .required(true), 21 | ) 22 | .arg( 23 | Arg::with_name("name") 24 | .long("name") 25 | .short("n") 26 | .value_name("NAME") 27 | .help("The name of the new Azure Function. You may specify this as instead (i.e., without typing --name).") 28 | ) 29 | .arg( 30 | Arg::with_name("connection") 31 | .long("connection") 32 | .short("c") 33 | .value_name("CONNECTION") 34 | .help("The name of the connection setting to use for the Event Hub trigger.") 35 | .required(true), 36 | ) 37 | .arg( 38 | Arg::with_name("hub_name") 39 | .long("hub-name") 40 | .short("h") 41 | .value_name("HUBNAME") 42 | .help("The name of the Event Hub."), 43 | ) 44 | } 45 | 46 | pub fn execute(&self, quiet: bool) -> Result<(), String> { 47 | let data = json!({ 48 | "name": self.name, 49 | "connection": self.connection, 50 | "hub_name": self.hub_name, 51 | }); 52 | 53 | create_function(self.name, "eventhub.rs", &data, quiet) 54 | } 55 | } 56 | 57 | impl<'a> From<&'a ArgMatches<'a>> for EventHub<'a> { 58 | fn from(args: &'a ArgMatches<'a>) -> Self { 59 | EventHub { 60 | name: args 61 | .value_of("positional-name") 62 | .unwrap_or_else(|| args.value_of("name").expect("A NAME argument is needed")), 63 | connection: args.value_of("connection").unwrap(), 64 | hub_name: args.value_of("hub_name").unwrap_or(""), 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /azure-functions-sdk/src/commands/new/http.rs: -------------------------------------------------------------------------------- 1 | use crate::commands::new::create_function; 2 | use clap::{App, Arg, ArgMatches, SubCommand}; 3 | use serde_json::json; 4 | 5 | pub struct Http<'a> { 6 | name: &'a str, 7 | auth_level: &'a str, 8 | } 9 | 10 | impl<'a> Http<'a> { 11 | pub fn create_subcommand<'b>() -> App<'a, 'b> { 12 | SubCommand::with_name("http") 13 | .about("Creates a new HTTP triggered Azure Function.") 14 | .arg( 15 | Arg::with_name("positional-name") 16 | .value_name("NAME") 17 | .help("The name of the new Azure Function. You may specify this as --name instead.") 18 | .conflicts_with("name") 19 | .required(true), 20 | ) 21 | .arg( 22 | Arg::with_name("name") 23 | .long("name") 24 | .short("n") 25 | .value_name("NAME") 26 | .help("The name of the new Azure Function. You may specify this as instead (i.e., without typing --name).") 27 | ) 28 | .arg( 29 | Arg::with_name("auth-level") 30 | .long("auth-level") 31 | .value_name("LEVEL") 32 | .possible_values(&["anonymous", "function", "admin"]) 33 | .help("The authentication level for the HTTP function. Default is 'function'."), 34 | ) 35 | } 36 | 37 | pub fn execute(&self, quiet: bool) -> Result<(), String> { 38 | let data = json!({ 39 | "name": self.name, 40 | "auth_level": self.auth_level 41 | }); 42 | 43 | create_function(self.name, "http.rs", &data, quiet) 44 | } 45 | } 46 | 47 | impl<'a> From<&'a ArgMatches<'a>> for Http<'a> { 48 | fn from(args: &'a ArgMatches<'a>) -> Self { 49 | Http { 50 | name: args 51 | .value_of("positional-name") 52 | .unwrap_or_else(|| args.value_of("name").expect("A NAME argument is needed")), 53 | auth_level: match args.value_of("auth-level") { 54 | Some(level) => { 55 | if level == "function" { 56 | "" 57 | } else { 58 | level 59 | } 60 | } 61 | None => "", 62 | }, 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /azure-functions-sdk/src/commands/new/orchestration.rs: -------------------------------------------------------------------------------- 1 | use crate::commands::new::create_function; 2 | use clap::{App, Arg, ArgMatches, SubCommand}; 3 | use serde_json::json; 4 | 5 | pub struct Orchestration<'a> { 6 | name: &'a str, 7 | } 8 | 9 | impl<'a> Orchestration<'a> { 10 | pub fn create_subcommand<'b>() -> App<'a, 'b> { 11 | SubCommand::with_name("orchestration") 12 | .about("Creates a new Orchestration Function for Durable Functions.") 13 | .arg( 14 | Arg::with_name("positional-name") 15 | .value_name("NAME") 16 | .help("The name of the new Azure Function. You may specify this as --name instead.") 17 | .conflicts_with("name") 18 | .required(true), 19 | ) 20 | .arg( 21 | Arg::with_name("name") 22 | .long("name") 23 | .short("n") 24 | .value_name("NAME") 25 | .help("The name of the new Azure Function. You may specify this as instead (i.e., without typing --name).") 26 | ) 27 | } 28 | 29 | pub fn execute(&self, quiet: bool) -> Result<(), String> { 30 | let data = json!({ 31 | "name": self.name, 32 | }); 33 | 34 | create_function(self.name, "orchestration.rs", &data, quiet) 35 | } 36 | } 37 | 38 | impl<'a> From<&'a ArgMatches<'a>> for Orchestration<'a> { 39 | fn from(args: &'a ArgMatches<'a>) -> Self { 40 | Orchestration { 41 | name: args 42 | .value_of("positional-name") 43 | .unwrap_or_else(|| args.value_of("name").expect("A NAME argument is needed")), 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /azure-functions-sdk/src/commands/new/timer.rs: -------------------------------------------------------------------------------- 1 | use crate::commands::new::create_function; 2 | use clap::{App, Arg, ArgMatches, SubCommand}; 3 | use serde_json::json; 4 | 5 | pub struct Timer<'a> { 6 | name: &'a str, 7 | schedule: &'a str, 8 | } 9 | 10 | impl<'a> Timer<'a> { 11 | pub fn create_subcommand<'b>() -> App<'a, 'b> { 12 | SubCommand::with_name("timer") 13 | .about("Creates a new timer triggered Azure Function.") 14 | .arg( 15 | Arg::with_name("positional-name") 16 | .value_name("NAME") 17 | .help("The name of the new Azure Function. You may specify this as --name instead.") 18 | .conflicts_with("name") 19 | .required(true), 20 | ) 21 | .arg( 22 | Arg::with_name("name") 23 | .long("name") 24 | .short("n") 25 | .value_name("NAME") 26 | .help("The name of the new Azure Function. You may specify this as instead (i.e., without typing --name).") 27 | ) 28 | .arg( 29 | Arg::with_name("schedule") 30 | .long("schedule") 31 | .short("s") 32 | .value_name("SCHEDULE") 33 | .help("The timer schedule as a cron-expression.") 34 | .required(true), 35 | ) 36 | } 37 | 38 | pub fn execute(&self, quiet: bool) -> Result<(), String> { 39 | let data = json!({ 40 | "name": self.name, 41 | "schedule": self.schedule, 42 | }); 43 | 44 | create_function(self.name, "timer.rs", &data, quiet) 45 | } 46 | } 47 | 48 | impl<'a> From<&'a ArgMatches<'a>> for Timer<'a> { 49 | fn from(args: &'a ArgMatches<'a>) -> Self { 50 | Timer { 51 | name: args 52 | .value_of("positional-name") 53 | .unwrap_or_else(|| args.value_of("name").expect("A NAME argument is needed")), 54 | schedule: args.value_of("schedule").unwrap(), 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /azure-functions-sdk/src/templates/new-app/Dockerfile.hbs: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile-upstream:experimental 2 | 3 | FROM peterhuene/azure-functions-rs-build:{{crate_version}} AS build-image 4 | 5 | WORKDIR /src 6 | COPY . /src 7 | 8 | # Run with mounted cache 9 | RUN --mount=type=cache,target=/src/target \ 10 | --mount=type=cache,target=/usr/local/cargo/git \ 11 | --mount=type=cache,target=/usr/local/cargo/registry \ 12 | ["cargo", "run", "--release", "--", "init", "--script-root", "/home/site/wwwroot", "--sync-extensions"] 13 | 14 | FROM mcr.microsoft.com/azure-functions/base:2.0 as runtime-image 15 | 16 | FROM mcr.microsoft.com/dotnet/core/runtime-deps:2.2 17 | 18 | ENV AzureWebJobsScriptRoot=/home/site/wwwroot \ 19 | HOME=/home \ 20 | FUNCTIONS_WORKER_RUNTIME=Rust \ 21 | languageWorkers__workersDirectory=/home/site/wwwroot/workers 22 | 23 | # Copy the Azure Functions host from the runtime image 24 | COPY --from=runtime-image [ "/azure-functions-host", "/azure-functions-host" ] 25 | 26 | # Copy the script root contents from the build image 27 | COPY --from=build-image ["/home/site/wwwroot", "/home/site/wwwroot"] 28 | 29 | WORKDIR /home/site/wwwroot 30 | CMD [ "/azure-functions-host/Microsoft.Azure.WebJobs.Script.WebHost" ] 31 | -------------------------------------------------------------------------------- /azure-functions-sdk/src/templates/new-app/dockerignore.hbs: -------------------------------------------------------------------------------- 1 | target/ 2 | Cargo.lock 3 | .vscode/ 4 | .git/ 5 | -------------------------------------------------------------------------------- /azure-functions-sdk/src/templates/new-app/functions_mod.rs.hbs: -------------------------------------------------------------------------------- 1 | // WARNING: This file is regenerated by the `cargo func new` command. 2 | 3 | {{#each modules}}mod {{this}}; 4 | {{/each~}} 5 | {{~#if modules}} 6 | {{/if~}} 7 | // Export the Azure Functions here. 8 | azure_functions::export! { {{~#each exports}} 9 | {{this}},{{else~}}{{/each~}}{{~#if exports}} 10 | {{/if~}} 11 | } 12 | -------------------------------------------------------------------------------- /azure-functions-sdk/src/templates/new-app/host.json.hbs: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "logging": { 4 | "logLevel": { 5 | "default": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /azure-functions-sdk/src/templates/new-app/launch.json.hbs: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "lldb", 6 | "request": "attach", 7 | "name": "Debug", 8 | "windows": { 9 | "program": "{{name}}.exe" 10 | }, 11 | "program": "{{name}}", 12 | "preLaunchTask": "Launch Azure Functions Application", 13 | "postDebugTask": "Terminate Azure Functions Application" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /azure-functions-sdk/src/templates/new-app/local.settings.json.hbs: -------------------------------------------------------------------------------- 1 | { 2 | "IsEncrypted": false, 3 | "Values": { 4 | "FUNCTIONS_WORKER_RUNTIME": "Rust", 5 | "languageWorkers:workersDirectory": "workers" 6 | }, 7 | "ConnectionStrings": { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /azure-functions-sdk/src/templates/new-app/main.rs.hbs: -------------------------------------------------------------------------------- 1 | mod functions; 2 | 3 | fn main() { 4 | azure_functions::worker_main(std::env::args(), functions::EXPORTS); 5 | } 6 | -------------------------------------------------------------------------------- /azure-functions-sdk/src/templates/new-app/tasks.json.hbs: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "Launch Azure Functions Application", 6 | "type": "shell", 7 | "command": "cargo", 8 | "args": [ 9 | "func", 10 | "run" 11 | ], 12 | "presentation": { 13 | "reveal": "always", 14 | "clear": true, 15 | "focus": true 16 | }, 17 | "problemMatcher": [ 18 | { 19 | "owner": "azureFunctions", 20 | "pattern": [ 21 | { 22 | "regexp": "\\b\\B", 23 | "file": 1, 24 | "location": 2, 25 | "message": 3 26 | } 27 | ], 28 | "background": { 29 | "activeOnStart": true, 30 | "beginsPattern": "^Azure Functions Core Tools", 31 | "endsPattern": "^Application started." 32 | } 33 | } 34 | ], 35 | "isBackground": true 36 | }, 37 | { 38 | "label": "Terminate Azure Functions Application", 39 | "type": "process", 40 | "command":"${command:workbench.action.tasks.terminate}", 41 | "args": [ 42 | "Launch Azure Functions Application" 43 | ] 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /azure-functions-sdk/src/templates/new/activity.rs.hbs: -------------------------------------------------------------------------------- 1 | use azure_functions::{bindings::DurableActivityContext, durable::ActivityOutput, func}; 2 | 3 | #[func] 4 | pub async fn {{name}}(context: DurableActivityContext) -> ActivityOutput { 5 | "Hello from {{name}}!".into() 6 | } 7 | -------------------------------------------------------------------------------- /azure-functions-sdk/src/templates/new/blob.rs.hbs: -------------------------------------------------------------------------------- 1 | use azure_functions::{ 2 | bindings::BlobTrigger, 3 | func, 4 | }; 5 | 6 | #[func] 7 | #[binding(name = "trigger", path = "{{path}}")] 8 | pub fn {{name}}(trigger: BlobTrigger) { 9 | // trigger.path has the path to the blob that triggered the function 10 | // trigger.blob has the contents of the blob 11 | } 12 | -------------------------------------------------------------------------------- /azure-functions-sdk/src/templates/new/cosmosdb.rs.hbs: -------------------------------------------------------------------------------- 1 | use azure_functions::{bindings::CosmosDbTrigger, func}; 2 | 3 | #[func] 4 | #[binding( 5 | name = "trigger", 6 | connection = "{{connection}}", 7 | database_name = "{{database}}", 8 | collection_name = "{{collection}}" 9 | )] 10 | pub fn {{name}}(trigger: CosmosDbTrigger) { 11 | // trigger.documents contain the Cosmos DB documents that triggered the function 12 | } 13 | -------------------------------------------------------------------------------- /azure-functions-sdk/src/templates/new/eventgrid.rs.hbs: -------------------------------------------------------------------------------- 1 | use azure_functions::{ 2 | bindings::EventGridEvent, 3 | func, 4 | }; 5 | 6 | #[func] 7 | pub fn {{name}}(event: EventGridEvent) { 8 | // event.data has the event data JSON 9 | } 10 | -------------------------------------------------------------------------------- /azure-functions-sdk/src/templates/new/eventhub.rs.hbs: -------------------------------------------------------------------------------- 1 | use azure_functions::{ 2 | bindings::EventHubTrigger, 3 | func, 4 | }; 5 | 6 | #[func] 7 | {{#if hub_name~}} 8 | #[binding(name = "trigger", connection = "{{connection}}", event_hub_name = "{{hub_name}}")] 9 | {{else~}} 10 | #[binding(name = "trigger", connection = "{{connection}}")] 11 | {{/if~}} 12 | pub fn {{name}}(trigger: EventHubTrigger) { 13 | // trigger.message contains the message posted to the Event Hub 14 | } 15 | -------------------------------------------------------------------------------- /azure-functions-sdk/src/templates/new/http.rs.hbs: -------------------------------------------------------------------------------- 1 | use azure_functions::{ 2 | bindings::{HttpRequest, HttpResponse}, 3 | func, 4 | }; 5 | 6 | #[func] 7 | {{#if auth_level~}} 8 | #[binding(name = "req", auth_level = "{{auth_level}}")] 9 | {{/if~}} 10 | pub fn {{name}}(req: HttpRequest) -> HttpResponse { 11 | "Hello from Rust!".into() 12 | } 13 | -------------------------------------------------------------------------------- /azure-functions-sdk/src/templates/new/orchestration.rs.hbs: -------------------------------------------------------------------------------- 1 | use azure_functions::{bindings::DurableOrchestrationContext, durable::OrchestrationOutput, func}; 2 | 3 | #[func] 4 | pub async fn {{name}}(context: DurableOrchestrationContext) -> OrchestrationOutput { 5 | "Hello from {{name}}!".into() 6 | } 7 | -------------------------------------------------------------------------------- /azure-functions-sdk/src/templates/new/queue.rs.hbs: -------------------------------------------------------------------------------- 1 | use azure_functions::{ 2 | bindings::QueueTrigger, 3 | func, 4 | }; 5 | 6 | #[func] 7 | #[binding(name = "trigger", queue_name = "{{queue_name}}")] 8 | pub fn {{name}}(trigger: QueueTrigger) { 9 | // trigger.message contains the message posted to the queue 10 | } 11 | -------------------------------------------------------------------------------- /azure-functions-sdk/src/templates/new/servicebus.rs.hbs: -------------------------------------------------------------------------------- 1 | use azure_functions::{bindings::ServiceBusTrigger, func}; 2 | 3 | #[func] 4 | {{#if queue~}} 5 | #[binding(name = "trigger", connection = "{{connection}}", queue_name = "{{queue}}")] 6 | {{else~}} 7 | #[binding(name = "trigger", connection = "{{connection}}", topic_name = "{{topic}}", subscription_name = "{{subscription}}")] 8 | {{/if~}} 9 | pub fn {{name}}(trigger: ServiceBusTrigger) { 10 | // trigger.message contains the posted message 11 | } 12 | -------------------------------------------------------------------------------- /azure-functions-sdk/src/templates/new/timer.rs.hbs: -------------------------------------------------------------------------------- 1 | use azure_functions::{ 2 | bindings::TimerInfo, 3 | func, 4 | }; 5 | 6 | #[func] 7 | #[binding(name = "trigger", schedule = "{{schedule}}")] 8 | pub fn {{name}}(trigger: TimerInfo) { 9 | // function will execute at the scheduled interval 10 | } 11 | -------------------------------------------------------------------------------- /azure-functions-sdk/src/util.rs: -------------------------------------------------------------------------------- 1 | use colored::Colorize; 2 | use handlebars::Handlebars; 3 | use std::fs::{create_dir_all, OpenOptions}; 4 | use std::io::Write; 5 | use std::path::Path; 6 | 7 | pub fn print_running(message: &str) { 8 | print!( 9 | "{} {}", 10 | if cfg!(windows) { "->" } else { "️🚀" }.cyan(), 11 | message 12 | ); 13 | } 14 | 15 | pub fn print_success() { 16 | println!(" {}", if cfg!(windows) { "OK" } else { "️✓" }.green()); 17 | } 18 | 19 | pub fn print_failure() { 20 | println!(" {}", if cfg!(windows) { "FAIL" } else { "✗" }.red()); 21 | } 22 | 23 | pub fn create_from_template( 24 | templates: &Handlebars, 25 | template_name: &str, 26 | base_path: &str, 27 | relative_path: &str, 28 | data: &serde_json::Value, 29 | ) -> Result<(), String> { 30 | let output_path = Path::new(base_path).join(relative_path); 31 | 32 | if let Some(dir) = output_path.parent() { 33 | create_dir_all(&dir) 34 | .unwrap_or_else(|_| panic!("failed to create directory for '{}'", relative_path)); 35 | } 36 | 37 | let mut file = OpenOptions::new() 38 | .create(true) 39 | .write(true) 40 | .truncate(true) 41 | .open(output_path) 42 | .map_err(|e| format!("failed to create '{}': {}", relative_path.cyan(), e))?; 43 | 44 | file.write_all( 45 | templates 46 | .render(template_name, data) 47 | .map_err(|e| format!("failed to render '{}': {}", relative_path.cyan(), e))? 48 | .as_bytes(), 49 | ) 50 | .map_err(|e| format!("failed to write {}: {}", relative_path.cyan(), e))?; 51 | 52 | Ok(()) 53 | } 54 | 55 | pub fn last_segment_in_path(path: &syn::Path) -> &syn::PathSegment { 56 | path.segments 57 | .iter() 58 | .last() 59 | .expect("expected at least one segment in path") 60 | } 61 | -------------------------------------------------------------------------------- /azure-functions-shared-codegen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "azure-functions-shared-codegen" 3 | license = "MIT" 4 | version = "0.11.0" 5 | authors = ["Peter Huene "] 6 | description = "Azure Functions for Rust shared code generation support." 7 | repository = "https://github.com/peterhuene/azure-functions-rs/" 8 | homepage = "https://functions.rs" 9 | edition = "2018" 10 | 11 | [lib] 12 | proc-macro = true 13 | 14 | [dependencies] 15 | quote = "1.0.2" 16 | syn = { version = "1.0.7", features = ["full"] } 17 | proc-macro2 = { version = "1.0.6" } 18 | 19 | [features] 20 | unstable = [] -------------------------------------------------------------------------------- /azure-functions-shared-codegen/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Azure Functions for Rust 2 | //! 3 | //! This crate supports code generation for the `azure-functions-shared` crate. 4 | #![deny(unused_extern_crates)] 5 | #![recursion_limit = "128"] 6 | #![cfg_attr(feature = "unstable", feature(proc_macro_diagnostic))] 7 | extern crate proc_macro; 8 | 9 | mod binding; 10 | 11 | use binding::binding_impl; 12 | use proc_macro2::{Delimiter, Span}; 13 | use syn::{ 14 | buffer::TokenBuffer, spanned::Spanned, Attribute, AttributeArgs, Ident, Lit, Meta, NestedMeta, 15 | Path, PathSegment, 16 | }; 17 | 18 | fn last_segment_in_path(path: &Path) -> &PathSegment { 19 | path.segments 20 | .iter() 21 | .last() 22 | .expect("expected at least one segment in path") 23 | } 24 | 25 | fn parse_attribute_args(attr: &Attribute) -> AttributeArgs { 26 | let span = attr.span(); 27 | let stream: proc_macro::TokenStream = match TokenBuffer::new2(attr.tokens.clone()) 28 | .begin() 29 | .group(Delimiter::Parenthesis) 30 | { 31 | Some((tree, _, _)) => tree.token_stream().into(), 32 | None => macro_panic(span, "failed to parse attribute"), 33 | }; 34 | 35 | syn::parse_macro_input::parse::(stream) 36 | .map_err(move |e| macro_panic(span, format!("failed to parse attribute arguments: {}", e))) 37 | .unwrap() 38 | } 39 | 40 | fn iter_attribute_args(args: &[NestedMeta], mut callback: F) 41 | where 42 | F: FnMut(&Ident, &Lit) -> bool, 43 | { 44 | for arg in args { 45 | match arg { 46 | NestedMeta::Meta(m) => { 47 | match m { 48 | Meta::NameValue(nvp) => { 49 | if !callback(&last_segment_in_path(&nvp.path).ident, &nvp.lit) { 50 | return; 51 | } 52 | } 53 | _ => macro_panic(m.span(), "expected name-value pair for an argument"), 54 | }; 55 | } 56 | _ => macro_panic(arg.span(), "expected a name-vaule pair for an argument"), 57 | }; 58 | } 59 | } 60 | 61 | #[cfg(feature = "unstable")] 62 | fn macro_panic(span: Span, message: T) -> ! 63 | where 64 | T: AsRef, 65 | { 66 | span.unstable().error(message.as_ref()).emit(); 67 | panic!("aborting due to previous error"); 68 | } 69 | 70 | #[cfg(not(feature = "unstable"))] 71 | fn macro_panic(_: Span, message: T) -> ! 72 | where 73 | T: AsRef, 74 | { 75 | panic!("{}", message.as_ref()); 76 | } 77 | 78 | #[proc_macro_attribute] 79 | pub fn binding( 80 | args: proc_macro::TokenStream, 81 | input: proc_macro::TokenStream, 82 | ) -> proc_macro::TokenStream { 83 | binding_impl(args, input) 84 | } 85 | -------------------------------------------------------------------------------- /azure-functions-shared/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "azure-functions-shared" 3 | license = "MIT" 4 | version = "0.11.0" 5 | authors = ["Peter Huene "] 6 | description = "Implementations shared between the azure-functions-codegen and azure-functions crates." 7 | repository = "https://github.com/peterhuene/azure-functions-rs/" 8 | homepage = "https://functions.rs" 9 | edition = "2018" 10 | 11 | [dependencies] 12 | azure-functions-shared-codegen = { version = "0.11.0", path = "../azure-functions-shared-codegen" } 13 | tonic = "0.1.0-alpha.5" 14 | prost = "0.5" 15 | prost-types = "0.5" 16 | bytes = "0.4" 17 | serde = {version = "1.0.102", features = ["derive"] } 18 | serde_json = "1.0.41" 19 | quote = "1.0.2" 20 | syn = { version = "1.0.7", features = ["full"] } 21 | proc-macro2 = { version = "1.0.6" } 22 | lazy_static = "1.4.0" 23 | 24 | [build-dependencies] 25 | tonic-build = "0.1.0-alpha.3" 26 | 27 | [features] 28 | default = [] 29 | compile_protobufs = [] 30 | unstable = ["azure-functions-shared-codegen/unstable"] 31 | -------------------------------------------------------------------------------- /azure-functions-shared/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::fs; 3 | use std::path::{Path, PathBuf}; 4 | use std::process::Command; 5 | 6 | const OUT_DIR_VAR: &str = "OUT_DIR"; 7 | const CACHE_DIR_NAME: &str = "cache"; 8 | const PROTOBUF_INPUT_FILES: &[&str] = &["FunctionRpc.proto"]; 9 | const OUTPUT_FILES: &[&str] = &["azure_functions_rpc_messages.rs"]; 10 | 11 | fn format_source(path: &Path) { 12 | Command::new("rustfmt") 13 | .arg(path.to_str().unwrap()) 14 | .output() 15 | .expect("Failed to format generated source"); 16 | } 17 | 18 | fn compile_protobufs(out_dir: &PathBuf, cache_dir: &PathBuf) { 19 | tonic_build::configure() 20 | .build_server(false) 21 | .compile(PROTOBUF_INPUT_FILES, &["protobuf/src/proto"]) 22 | .unwrap_or_else(|e| panic!("protobuf compilation failed: {}", e)); 23 | 24 | for file in OUTPUT_FILES { 25 | let cached_output = cache_dir.join(file); 26 | 27 | fs::copy(out_dir.join(file), &cached_output).expect(&format!( 28 | "can't update cache file '{}'", 29 | cached_output.display() 30 | )); 31 | 32 | format_source(&cached_output); 33 | } 34 | } 35 | 36 | fn use_cached_files(out_dir: &PathBuf, cache_dir: &PathBuf) { 37 | for file in OUTPUT_FILES { 38 | fs::copy(cache_dir.join(file), out_dir.join(file)).expect(&format!( 39 | "can't copy cache file '{}' to output directory", 40 | file 41 | )); 42 | } 43 | } 44 | 45 | fn main() { 46 | for file in PROTOBUF_INPUT_FILES { 47 | println!("cargo:rerun-if-changed=protobuf/src/proto/{}", file); 48 | } 49 | 50 | let out_dir = PathBuf::from(env::var(OUT_DIR_VAR).unwrap()); 51 | 52 | let cache_dir = env::current_dir() 53 | .expect("couldn't determine current working directory") 54 | .join(CACHE_DIR_NAME); 55 | 56 | fs::create_dir_all(&cache_dir).expect("failed to create cache directory"); 57 | 58 | if cfg!(feature = "compile_protobufs") { 59 | compile_protobufs(&out_dir, &cache_dir); 60 | } else { 61 | use_cached_files(&out_dir, &cache_dir); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /azure-functions-shared/cache/README.md: -------------------------------------------------------------------------------- 1 | # Protobuf Cache 2 | 3 | This directory exists to cache the output of a previous protobuf compilation. 4 | 5 | If you do not have `protoc` on your PATH, the cached protobuf definitions will be used. 6 | 7 | This is primarily used for crate publishing so that [docs.rs](https://docs.rs) can 8 | generate documentation without having a protobuf compiler installed. 9 | -------------------------------------------------------------------------------- /azure-functions-shared/src/codegen/bindings/event_grid_trigger.rs: -------------------------------------------------------------------------------- 1 | use azure_functions_shared_codegen::binding; 2 | use std::borrow::Cow; 3 | 4 | #[binding(name = "eventGridTrigger", direction = "in")] 5 | pub struct EventGridTrigger { 6 | #[field(camel_case_value = true)] 7 | pub name: Cow<'static, str>, 8 | } 9 | 10 | #[cfg(test)] 11 | mod tests { 12 | use super::*; 13 | use crate::codegen::tests::should_panic; 14 | use proc_macro2::{Span, TokenStream}; 15 | use quote::ToTokens; 16 | use serde_json::to_string; 17 | use syn::{parse_str, NestedMeta}; 18 | 19 | #[test] 20 | fn it_serializes_to_json() { 21 | let binding = EventGridTrigger { 22 | name: Cow::from("foo"), 23 | }; 24 | 25 | assert_eq!( 26 | to_string(&binding).unwrap(), 27 | r#"{"type":"eventGridTrigger","direction":"in","name":"foo"}"# 28 | ); 29 | } 30 | 31 | #[test] 32 | fn it_parses_attribute_arguments() { 33 | let binding: EventGridTrigger = ( 34 | vec![parse_str::(r#"name = "foo""#).unwrap()], 35 | Span::call_site(), 36 | ) 37 | .into(); 38 | 39 | assert_eq!(binding.name.as_ref(), "foo"); 40 | } 41 | 42 | #[test] 43 | fn it_requires_the_name_attribute_argument() { 44 | should_panic( 45 | || { 46 | let _: EventGridTrigger = (vec![], Span::call_site()).into(); 47 | }, 48 | "the 'name' argument is required for this binding", 49 | ); 50 | } 51 | 52 | #[test] 53 | fn it_requires_the_name_attribute_be_a_string() { 54 | should_panic( 55 | || { 56 | let _: EventGridTrigger = ( 57 | vec![parse_str::(r#"name = false"#).unwrap()], 58 | Span::call_site(), 59 | ) 60 | .into(); 61 | }, 62 | "expected a literal string value for the 'name' argument", 63 | ); 64 | } 65 | 66 | #[test] 67 | fn it_converts_to_tokens() { 68 | let binding = EventGridTrigger { 69 | name: Cow::from("foo"), 70 | }; 71 | 72 | let mut stream = TokenStream::new(); 73 | binding.to_tokens(&mut stream); 74 | let mut tokens = stream.to_string(); 75 | tokens.retain(|c| c != ' '); 76 | 77 | assert_eq!( 78 | tokens, 79 | r#"::azure_functions::codegen::bindings::EventGridTrigger{name:::std::borrow::Cow::Borrowed("foo"),}"# 80 | ); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /azure-functions-shared/src/codegen/bindings/http.rs: -------------------------------------------------------------------------------- 1 | use azure_functions_shared_codegen::binding; 2 | use std::borrow::Cow; 3 | 4 | #[binding(name = "http", direction = "out")] 5 | pub struct Http { 6 | #[field(camel_case_value = true)] 7 | pub name: Cow<'static, str>, 8 | } 9 | 10 | #[cfg(test)] 11 | mod tests { 12 | use super::*; 13 | use crate::codegen::tests::should_panic; 14 | use proc_macro2::{Span, TokenStream}; 15 | use quote::ToTokens; 16 | use serde_json::to_string; 17 | use syn::{parse_str, NestedMeta}; 18 | 19 | #[test] 20 | fn it_serializes_to_json() { 21 | let binding = Http { 22 | name: Cow::from("foo"), 23 | }; 24 | 25 | assert_eq!( 26 | to_string(&binding).unwrap(), 27 | r#"{"type":"http","direction":"out","name":"foo"}"# 28 | ); 29 | } 30 | 31 | #[test] 32 | fn it_parses_attribute_arguments() { 33 | let binding: Http = ( 34 | vec![parse_str::(r#"name = "foo""#).unwrap()], 35 | Span::call_site(), 36 | ) 37 | .into(); 38 | 39 | assert_eq!(binding.name.as_ref(), "foo"); 40 | } 41 | 42 | #[test] 43 | fn it_requires_the_name_attribute_argument() { 44 | should_panic( 45 | || { 46 | let _: Http = (vec![], Span::call_site()).into(); 47 | }, 48 | "the 'name' argument is required for this binding", 49 | ); 50 | } 51 | 52 | #[test] 53 | fn it_requires_the_name_attribute_be_a_string() { 54 | should_panic( 55 | || { 56 | let _: Http = ( 57 | vec![parse_str::(r#"name = false"#).unwrap()], 58 | Span::call_site(), 59 | ) 60 | .into(); 61 | }, 62 | "expected a literal string value for the 'name' argument", 63 | ); 64 | } 65 | 66 | #[test] 67 | fn it_converts_to_tokens() { 68 | let binding = Http { 69 | name: Cow::from("foo"), 70 | }; 71 | 72 | let mut stream = TokenStream::new(); 73 | binding.to_tokens(&mut stream); 74 | let mut tokens = stream.to_string(); 75 | tokens.retain(|c| c != ' '); 76 | 77 | assert_eq!( 78 | tokens, 79 | r#"::azure_functions::codegen::bindings::Http{name:::std::borrow::Cow::Borrowed("foo"),}"# 80 | ); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /azure-functions-shared/src/codegen/quotable.rs: -------------------------------------------------------------------------------- 1 | use crate::codegen::bindings::Direction; 2 | use proc_macro2::TokenStream; 3 | use quote::{quote, ToTokens}; 4 | use std::borrow::Cow; 5 | 6 | pub struct QuotableBorrowedStr<'a>(pub &'a str); 7 | 8 | impl ToTokens for QuotableBorrowedStr<'_> { 9 | fn to_tokens(&self, tokens: &mut TokenStream) { 10 | let s = self.0; 11 | quote!(::std::borrow::Cow::Borrowed(#s)).to_tokens(tokens); 12 | } 13 | } 14 | 15 | pub struct QuotableStrArray<'a>(pub &'a [Cow<'a, str>]); 16 | 17 | impl ToTokens for QuotableStrArray<'_> { 18 | fn to_tokens(&self, tokens: &mut TokenStream) { 19 | let strings = self.0.iter().map(|s| QuotableBorrowedStr(s)); 20 | quote!(::std::borrow::Cow::Borrowed(&[#(#strings,)*])).to_tokens(tokens); 21 | } 22 | } 23 | 24 | pub struct QuotableOption(pub Option); 25 | 26 | impl ToTokens for QuotableOption { 27 | fn to_tokens(&self, tokens: &mut TokenStream) { 28 | match &self.0 { 29 | Some(inner) => quote!(Some(#inner)), 30 | None => quote!(None), 31 | } 32 | .to_tokens(tokens); 33 | } 34 | } 35 | 36 | pub struct QuotableDirection(pub Direction); 37 | 38 | impl ToTokens for QuotableDirection { 39 | fn to_tokens(&self, tokens: &mut TokenStream) { 40 | match self.0 { 41 | Direction::In => quote!(::azure_functions::codegen::bindings::Direction::In), 42 | Direction::InOut => quote!(::azure_functions::codegen::bindings::Direction::InOut), 43 | Direction::Out => quote!(::azure_functions::codegen::bindings::Direction::Out), 44 | } 45 | .to_tokens(tokens); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /azure-functions-shared/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Azure Functions for Rust 2 | //! 3 | //! This crate shares types between the `azure-functions-codegen` and `azure-functions` crates. 4 | #![recursion_limit = "128"] 5 | #![cfg_attr(feature = "unstable", feature(proc_macro_diagnostic))] 6 | #![deny(missing_docs)] 7 | #![deny(unused_extern_crates)] 8 | #![allow(clippy::large_enum_variant)] 9 | 10 | #[doc(hidden)] 11 | pub mod codegen; 12 | #[doc(hidden)] 13 | pub mod util; 14 | 15 | #[doc(hidden)] 16 | #[allow(clippy::type_repetition_in_bounds)] 17 | pub mod rpc { 18 | tonic::include_proto!("azure_functions_rpc_messages"); 19 | } 20 | -------------------------------------------------------------------------------- /azure-functions-shared/src/util.rs: -------------------------------------------------------------------------------- 1 | pub fn to_camel_case(input: &str) -> String { 2 | let mut result = String::new(); 3 | let mut capitalize = false; 4 | let mut first = true; 5 | for ch in input.chars() { 6 | if ch == '_' { 7 | capitalize = true; 8 | } else { 9 | result.push(if capitalize && !first { 10 | ch.to_ascii_uppercase() 11 | } else { 12 | ch 13 | }); 14 | first = false; 15 | capitalize = false; 16 | } 17 | } 18 | result 19 | } 20 | -------------------------------------------------------------------------------- /azure-functions/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "azure-functions" 3 | license = "MIT" 4 | version = "0.11.0" 5 | authors = ["Peter Huene "] 6 | description = "Azure Functions for Rust" 7 | repository = "https://github.com/peterhuene/azure-functions-rs/" 8 | homepage = "https://functions.rs" 9 | edition = "2018" 10 | 11 | [dependencies] 12 | azure-functions-shared = { version = "0.11.0", path = "../azure-functions-shared" } 13 | azure-functions-codegen = { version = "0.11.0", path = "../azure-functions-codegen" } 14 | azure-functions-durable = { version = "0.11.0", path = "../azure-functions-durable" } 15 | http = "0.1" 16 | tonic = "0.1.0-alpha.5" 17 | log = { version = "0.4.8", features = ["std"] } 18 | futures-preview = "0.3.0-alpha.19" 19 | clap = "2.33.0" 20 | tokio = "0.2.0-alpha.6" 21 | tokio-executor = "0.2.0-alpha.6" 22 | serde = {version = "1.0.102", features = ["derive"] } 23 | serde_json = "1.0.41" 24 | serde_repr = "0.1.5" 25 | chrono = { version = "0.4.9", features = ["serde"] } 26 | xml-rs = "0.8.0" 27 | lazy_static = "1.4.0" 28 | tempfile = "3.1.0" 29 | ctrlc = "3.1.3" 30 | backtrace = "0.3.39" 31 | fs_extra = "1.1.0" 32 | semver = "0.9.0" 33 | sha1 = "0.6.0" 34 | uuid = { version = "0.8.1", features = ["v5"] } 35 | 36 | [features] 37 | unstable = ["azure-functions-codegen/unstable", "azure-functions-shared/unstable"] 38 | 39 | [dev-dependencies] 40 | matches = "0.1.8" 41 | -------------------------------------------------------------------------------- /azure-functions/src/backtrace.rs: -------------------------------------------------------------------------------- 1 | use backtrace::BacktraceFrame; 2 | use std::env; 3 | use std::fmt; 4 | 5 | pub struct Backtrace { 6 | inner: backtrace::Backtrace, 7 | } 8 | 9 | impl Backtrace { 10 | pub fn new() -> Backtrace { 11 | if !Backtrace::is_enabled() { 12 | return Backtrace { 13 | inner: Vec::::new().into(), 14 | }; 15 | } 16 | 17 | let mut found_start = false; 18 | let mut found_end = false; 19 | 20 | // This attempts to filter to only the frames relevant to the Azure Function 21 | let frames: Vec = backtrace::Backtrace::new() 22 | .frames() 23 | .iter() 24 | .filter_map(|frame| { 25 | if found_end { 26 | return None; 27 | } 28 | 29 | for symbol in frame.symbols() { 30 | if let Some(name) = symbol.name() { 31 | let name = format!("{}", name); 32 | 33 | // Check for the start (i.e. where the panic starts) 34 | if !found_start { 35 | if name.starts_with("std::panicking::begin_panic::") 36 | || name.starts_with("core::panicking::panic::") 37 | { 38 | found_start = true; 39 | } 40 | return None; 41 | } 42 | 43 | // Check for the end (the invoker frame) 44 | if !found_end && name.contains("::__invoke_") { 45 | found_end = true; 46 | return None; 47 | } 48 | } 49 | } 50 | 51 | Some(frame.clone()) 52 | }) 53 | .collect(); 54 | 55 | Backtrace { 56 | inner: frames.into(), 57 | } 58 | } 59 | 60 | pub fn is_enabled() -> bool { 61 | env::var("RUST_BACKTRACE").unwrap_or_else(|_| "0".to_owned()) == "1" 62 | } 63 | } 64 | 65 | impl fmt::Display for Backtrace { 66 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 67 | use std::fmt::Debug; 68 | 69 | if !Backtrace::is_enabled() { 70 | return write!( 71 | f, 72 | "\nNote: run with `RUST_BACKTRACE=1` environment variable to display a backtrace." 73 | ); 74 | } 75 | 76 | if self.inner.frames().is_empty() { 77 | return Ok(()); 78 | } 79 | 80 | writeln!(f)?; 81 | self.inner.fmt(f) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /azure-functions/src/bindings.rs: -------------------------------------------------------------------------------- 1 | //! Module for Azure Functions bindings. 2 | mod blob; 3 | mod blob_trigger; 4 | mod cosmos_db_document; 5 | mod cosmos_db_trigger; 6 | mod durable_activity_context; 7 | mod durable_orchestration_client; 8 | mod durable_orchestration_context; 9 | mod event_grid_event; 10 | mod event_hub_message; 11 | mod event_hub_trigger; 12 | mod generic_input; 13 | mod generic_output; 14 | mod generic_trigger; 15 | mod http_request; 16 | mod http_response; 17 | mod queue_message; 18 | mod queue_trigger; 19 | mod send_grid_message; 20 | mod service_bus_message; 21 | mod service_bus_trigger; 22 | mod signalr_connection_info; 23 | mod signalr_group_action; 24 | mod signalr_message; 25 | mod table; 26 | mod timer_info; 27 | mod twilio_sms_message; 28 | 29 | pub use self::blob::*; 30 | pub use self::blob_trigger::*; 31 | pub use self::cosmos_db_document::*; 32 | pub use self::cosmos_db_trigger::*; 33 | pub use self::durable_activity_context::*; 34 | pub use self::durable_orchestration_client::*; 35 | pub use self::durable_orchestration_context::*; 36 | pub use self::event_grid_event::*; 37 | pub use self::event_hub_message::*; 38 | pub use self::event_hub_trigger::*; 39 | pub use self::generic_input::*; 40 | pub use self::generic_output::*; 41 | pub use self::generic_trigger::*; 42 | pub use self::http_request::*; 43 | pub use self::http_response::*; 44 | pub use self::queue_message::*; 45 | pub use self::queue_trigger::*; 46 | pub use self::send_grid_message::*; 47 | pub use self::service_bus_message::*; 48 | pub use self::service_bus_trigger::*; 49 | pub use self::signalr_connection_info::*; 50 | pub use self::signalr_group_action::*; 51 | pub use self::signalr_message::*; 52 | pub use self::table::*; 53 | pub use self::timer_info::*; 54 | pub use self::twilio_sms_message::*; 55 | -------------------------------------------------------------------------------- /azure-functions/src/blob.rs: -------------------------------------------------------------------------------- 1 | //! Module for blob storage types. 2 | mod properties; 3 | 4 | pub use self::properties::*; 5 | -------------------------------------------------------------------------------- /azure-functions/src/commands.rs: -------------------------------------------------------------------------------- 1 | mod init; 2 | mod run; 3 | mod sync_extensions; 4 | 5 | pub use self::init::*; 6 | pub use self::run::*; 7 | pub use self::sync_extensions::*; 8 | -------------------------------------------------------------------------------- /azure-functions/src/durable/activity_output.rs: -------------------------------------------------------------------------------- 1 | use crate::rpc::{typed_data::Data, TypedData}; 2 | use serde_json::Value; 3 | use std::iter::FromIterator; 4 | 5 | /// Represents the output of a Durable Functions activity function. 6 | /// 7 | /// Supports conversion from JSON-compatible types. 8 | pub struct ActivityOutput(Value); 9 | 10 | impl From for ActivityOutput 11 | where 12 | T: Into, 13 | { 14 | fn from(t: T) -> Self { 15 | ActivityOutput(t.into()) 16 | } 17 | } 18 | 19 | impl FromIterator for ActivityOutput { 20 | fn from_iter(iter: I) -> Self 21 | where 22 | I: IntoIterator, 23 | { 24 | ActivityOutput(Value::from_iter(iter)) 25 | } 26 | } 27 | 28 | #[doc(hidden)] 29 | impl Into for ActivityOutput { 30 | fn into(self) -> TypedData { 31 | TypedData { 32 | data: Some(Data::Json(self.0.to_string())), 33 | } 34 | } 35 | } 36 | 37 | #[cfg(test)] 38 | mod tests { 39 | use super::*; 40 | use serde_json::json; 41 | 42 | #[test] 43 | fn it_converts_from_json() { 44 | let activity_output: ActivityOutput = json!({ "foo": "bar" }).into(); 45 | 46 | let data: TypedData = activity_output.into(); 47 | assert_eq!(data.data, Some(Data::Json(r#"{"foo":"bar"}"#.to_string()))); 48 | } 49 | 50 | #[test] 51 | fn it_converts_from_bool() { 52 | let activity_output: ActivityOutput = true.into(); 53 | 54 | let data: TypedData = activity_output.into(); 55 | assert_eq!(data.data, Some(Data::Json(true.to_string()))); 56 | } 57 | 58 | #[test] 59 | fn it_converts_from_string() { 60 | let activity_output: ActivityOutput = "foo".into(); 61 | 62 | let data: TypedData = activity_output.into(); 63 | assert_eq!(data.data, Some(Data::Json("\"foo\"".to_string()))); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /azure-functions/src/durable/history.rs: -------------------------------------------------------------------------------- 1 | use chrono::{DateTime, Utc}; 2 | use serde::Deserialize; 3 | use serde_json::Value; 4 | use serde_repr::Deserialize_repr; 5 | 6 | #[derive(Debug, Clone, Deserialize, PartialEq)] 7 | #[serde(rename_all = "PascalCase")] 8 | pub struct HistoryEvent { 9 | pub event_type: EventType, 10 | 11 | pub event_id: i32, 12 | 13 | pub is_played: bool, 14 | 15 | pub timestamp: DateTime, 16 | 17 | #[serde(skip)] 18 | pub is_processed: bool, 19 | 20 | // Used by: EventRaised, ExecutionStarted, SubOrchestrationInstanceCreated, TaskScheduled 21 | pub name: Option, 22 | 23 | // Used by: EventRaised, ExecutionStarted, SubOrchestrationInstanceCreated, TaskScheduled 24 | pub input: Option, 25 | 26 | // Used by: SubOrchestrationInstanceCompleted, TaskCompleted 27 | pub result: Option, 28 | 29 | // Used by: SubOrchestrationInstanceCompleted , SubOrchestrationInstanceFailed, TaskCompleted,TaskFailed 30 | pub task_scheduled_id: Option, 31 | 32 | // Used by: SubOrchestrationInstanceCreated 33 | pub instance_id: Option, 34 | 35 | // Used by: SubOrchestrationInstanceFailed, TaskFailed 36 | pub reason: Option, 37 | 38 | // Used by: SubOrchestrationInstanceFailed,TaskFailed 39 | pub details: Option, 40 | 41 | // Used by: TimerCreated, TimerFired 42 | pub fire_at: Option>, 43 | 44 | // Used by: TimerFired 45 | pub timer_id: Option, 46 | } 47 | 48 | #[derive(Debug, Copy, Clone, Deserialize_repr, PartialEq)] 49 | #[repr(u8)] 50 | pub enum EventType { 51 | ExecutionStarted = 0, 52 | ExecutionCompleted = 1, 53 | ExecutionFailed = 2, 54 | ExecutionTerminated = 3, 55 | TaskScheduled = 4, 56 | TaskCompleted = 5, 57 | TaskFailed = 6, 58 | SubOrchestrationInstanceCreated = 7, 59 | SubOrchestrationInstanceCompleted = 8, 60 | SubOrchestrationInstanceFailed = 9, 61 | TimerCreated = 10, 62 | TimerFired = 11, 63 | OrchestratorStarted = 12, 64 | OrchestratorCompleted = 13, 65 | EventSent = 14, 66 | EventRaised = 15, 67 | ContinueAsNew = 16, 68 | GenericEvent = 17, 69 | HistoryState = 18, 70 | } 71 | -------------------------------------------------------------------------------- /azure-functions/src/durable/orchestration_output.rs: -------------------------------------------------------------------------------- 1 | use crate::durable::IntoValue; 2 | use serde_json::Value; 3 | use std::iter::FromIterator; 4 | 5 | /// Represents the output of a Durable Functions orchestration function. 6 | /// 7 | /// Supports conversion from JSON-compatible types. 8 | pub struct OrchestrationOutput(Value); 9 | 10 | impl From for OrchestrationOutput 11 | where 12 | T: Into, 13 | { 14 | fn from(t: T) -> Self { 15 | OrchestrationOutput(t.into()) 16 | } 17 | } 18 | 19 | impl FromIterator for OrchestrationOutput { 20 | fn from_iter(iter: I) -> Self 21 | where 22 | I: IntoIterator, 23 | { 24 | OrchestrationOutput(Value::from_iter(iter)) 25 | } 26 | } 27 | 28 | impl IntoValue for OrchestrationOutput { 29 | fn into_value(self) -> Value { 30 | self.0 31 | } 32 | } 33 | 34 | #[cfg(test)] 35 | mod tests { 36 | use super::*; 37 | use serde_json::json; 38 | 39 | #[test] 40 | fn it_converts_from_json() { 41 | let orchestration_output: OrchestrationOutput = json!({"foo": "bar"}).into(); 42 | 43 | let data: Value = orchestration_output.into_value(); 44 | assert_eq!(data, json!({"foo": "bar"})); 45 | } 46 | 47 | #[test] 48 | fn it_converts_from_bool() { 49 | let orchestration_output: OrchestrationOutput = true.into(); 50 | 51 | let data: Value = orchestration_output.into_value(); 52 | assert_eq!(data, json!(true)); 53 | } 54 | 55 | #[test] 56 | fn it_converts_from_string() { 57 | let orchestration_output: OrchestrationOutput = "foo".into(); 58 | 59 | let data: Value = orchestration_output.into_value(); 60 | assert_eq!(data, json!("foo")); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /azure-functions/src/event_hub.rs: -------------------------------------------------------------------------------- 1 | //! Module for Event Hub types. 2 | mod partition_context; 3 | mod runtime_information; 4 | mod system_properties; 5 | 6 | pub use self::partition_context::*; 7 | pub use self::runtime_information::*; 8 | pub use self::system_properties::*; 9 | -------------------------------------------------------------------------------- /azure-functions/src/event_hub/partition_context.rs: -------------------------------------------------------------------------------- 1 | use crate::event_hub::RuntimeInformation; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | /// Encapsulates information related to an Event Hubs partition. 5 | #[derive(Debug, Serialize, Deserialize)] 6 | #[serde(rename_all = "PascalCase")] 7 | pub struct PartitionContext { 8 | /// The name of the consumer group. 9 | pub consumer_group_name: String, 10 | /// The path of the Event Hub. 11 | pub event_hub_path: String, 12 | /// The partition ID for the context. 13 | pub partition_id: String, 14 | /// The host owner for the partition. 15 | pub owner: String, 16 | /// The approximate receiver runtime information for a logical partition of the Event Hub. 17 | pub runtime_information: RuntimeInformation, 18 | } 19 | 20 | #[cfg(test)] 21 | mod tests { 22 | use super::*; 23 | use serde_json::from_str; 24 | 25 | #[test] 26 | fn it_deserializes_from_json() { 27 | const JSON: &'static str = r#"{"ConsumerGroupName":"$Default","EventHubPath":"test","PartitionId":"0","Owner":"40eeeb3a-3491-4072-ba37-59ecdc330b6e","RuntimeInformation":{"PartitionId":"0","LastSequenceNumber":0,"LastEnqueuedTimeUtc":"0001-01-01T00:00:00","LastEnqueuedOffset":null,"RetrievalTime":"0001-01-01T00:00:00"}}"#; 28 | 29 | let context: PartitionContext = 30 | from_str(JSON).expect("failed to parse partition context JSON data"); 31 | 32 | assert_eq!(context.consumer_group_name, "$Default"); 33 | assert_eq!(context.event_hub_path, "test"); 34 | assert_eq!(context.partition_id, "0"); 35 | assert_eq!(context.owner, "40eeeb3a-3491-4072-ba37-59ecdc330b6e"); 36 | assert_eq!(context.runtime_information.partition_id, "0"); 37 | assert_eq!(context.runtime_information.last_sequence_number, 0); 38 | assert_eq!( 39 | context.runtime_information.last_enqueued_time.to_rfc3339(), 40 | "0001-01-01T00:00:00+00:00" 41 | ); 42 | assert_eq!(context.runtime_information.last_enqueued_offset, None); 43 | assert_eq!( 44 | context.runtime_information.retrieval_time.to_rfc3339(), 45 | "0001-01-01T00:00:00+00:00" 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /azure-functions/src/event_hub/runtime_information.rs: -------------------------------------------------------------------------------- 1 | use crate::util::deserialize_datetime; 2 | use chrono::{DateTime, Utc}; 3 | use serde::{Deserialize, Serialize}; 4 | 5 | /// Represents the approximate receiver runtime information for a logical partition of an Event Hub. 6 | #[derive(Debug, Serialize, Deserialize)] 7 | #[serde(rename_all = "PascalCase")] 8 | pub struct RuntimeInformation { 9 | /// The partition ID for a logical partition of an Event Hub. 10 | pub partition_id: String, 11 | /// The last sequence number of the event within the partition stream of the Event Hub. 12 | pub last_sequence_number: i64, 13 | /// The enqueued time (in UTC) of the last event. 14 | #[serde( 15 | rename = "LastEnqueuedTimeUtc", 16 | deserialize_with = "deserialize_datetime" 17 | )] 18 | pub last_enqueued_time: DateTime, 19 | /// The offset of the last enqueued event. 20 | pub last_enqueued_offset: Option, 21 | /// The time when the runtime information was retrieved. 22 | #[serde(deserialize_with = "deserialize_datetime")] 23 | pub retrieval_time: DateTime, 24 | } 25 | 26 | #[cfg(test)] 27 | mod tests { 28 | use super::*; 29 | use serde_json::from_str; 30 | 31 | #[test] 32 | fn it_deserializes_from_json() { 33 | const JSON: &'static str = r#"{"PartitionId":"0","LastSequenceNumber":0,"LastEnqueuedTimeUtc":"0001-01-01T00:00:00","LastEnqueuedOffset":null,"RetrievalTime":"0001-01-01T00:00:00"}"#; 34 | 35 | let info: RuntimeInformation = 36 | from_str(JSON).expect("failed to parse runtime information JSON data"); 37 | assert_eq!(info.partition_id, "0"); 38 | assert_eq!(info.last_sequence_number, 0); 39 | assert_eq!( 40 | info.last_enqueued_time.to_rfc3339(), 41 | "0001-01-01T00:00:00+00:00" 42 | ); 43 | assert_eq!(info.last_enqueued_offset, None); 44 | assert_eq!( 45 | info.retrieval_time.to_rfc3339(), 46 | "0001-01-01T00:00:00+00:00" 47 | ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /azure-functions/src/event_hub/system_properties.rs: -------------------------------------------------------------------------------- 1 | use crate::util::deserialize_datetime; 2 | use chrono::{DateTime, Utc}; 3 | use serde::{Deserialize, Serialize}; 4 | 5 | /// Represents properties that are set by the Event Hubs service. 6 | #[derive(Debug, Serialize, Deserialize)] 7 | #[serde(rename_all = "PascalCase")] 8 | pub struct SystemProperties { 9 | /// The logical sequence number of the event within the partition stream of the Event Hub. 10 | pub sequence_number: i64, 11 | /// The data relative to the Event Hub partition stream. 12 | pub offset: String, 13 | /// The partition key of the corresponding partition. 14 | pub partition_key: Option, 15 | /// The enqueuing time of the message time in UTC. 16 | #[serde(rename = "EnqueuedTimeUtc", deserialize_with = "deserialize_datetime")] 17 | pub enqueued_time: DateTime, 18 | } 19 | 20 | #[cfg(test)] 21 | mod tests { 22 | use super::*; 23 | use serde_json::from_str; 24 | 25 | #[test] 26 | fn it_deserializes_from_json() { 27 | const JSON: &'static str = r#"{"SequenceNumber":3,"Offset":"152","PartitionKey":null,"EnqueuedTimeUtc":"2019-02-22T04:43:55.305Z"}"#; 28 | 29 | let properties: SystemProperties = 30 | from_str(JSON).expect("failed to parse system properties JSON data"); 31 | assert_eq!(properties.sequence_number, 3); 32 | assert_eq!(properties.offset, "152"); 33 | assert_eq!(properties.partition_key, None); 34 | assert_eq!( 35 | properties.enqueued_time.to_rfc3339(), 36 | "2019-02-22T04:43:55.305+00:00" 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /azure-functions/src/http.rs: -------------------------------------------------------------------------------- 1 | //! Module for HTTP types. 2 | mod body; 3 | mod response_builder; 4 | mod status; 5 | 6 | pub use self::body::*; 7 | pub use self::response_builder::*; 8 | pub use self::status::*; 9 | -------------------------------------------------------------------------------- /azure-functions/src/logger.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | rpc::{rpc_log, streaming_message::Content, RpcLog, StreamingMessage}, 3 | worker::Sender, 4 | }; 5 | use log::{Level, Log, Metadata, Record}; 6 | 7 | pub struct Logger { 8 | level: Level, 9 | sender: Sender, 10 | } 11 | 12 | impl Logger { 13 | pub fn new(level: Level, sender: Sender) -> Logger { 14 | Logger { level, sender } 15 | } 16 | } 17 | 18 | impl Log for Logger { 19 | fn enabled(&self, metadata: &Metadata) -> bool { 20 | metadata.level() <= self.level 21 | } 22 | 23 | fn log(&self, record: &Record) { 24 | if !self.enabled(record.metadata()) { 25 | return; 26 | } 27 | 28 | let mut event = RpcLog { 29 | level: match record.level() { 30 | Level::Trace => rpc_log::Level::Trace, 31 | Level::Debug => rpc_log::Level::Debug, 32 | Level::Info => rpc_log::Level::Information, 33 | Level::Warn => rpc_log::Level::Warning, 34 | Level::Error => rpc_log::Level::Error, 35 | } as i32, 36 | message: record.args().to_string(), 37 | ..Default::default() 38 | }; 39 | 40 | event.invocation_id = crate::context::CURRENT.with(|c| c.borrow().invocation_id.clone()); 41 | 42 | self.sender 43 | .unbounded_send(StreamingMessage { 44 | content: Some(Content::RpcLog(event)), 45 | ..Default::default() 46 | }) 47 | .unwrap_or(()); 48 | } 49 | 50 | fn flush(&self) {} 51 | } 52 | -------------------------------------------------------------------------------- /azure-functions/src/send_grid.rs: -------------------------------------------------------------------------------- 1 | //! Module for SendGrid types. 2 | mod attachment; 3 | mod bcc_settings; 4 | mod bypass_list_management; 5 | mod click_tracking; 6 | mod content; 7 | mod email_address; 8 | mod footer_settings; 9 | mod google_analytics; 10 | mod mail_settings; 11 | mod message_builder; 12 | mod open_tracking; 13 | mod personalization; 14 | mod sandbox_mode; 15 | mod spam_check; 16 | mod subscription_tracking; 17 | mod tracking_settings; 18 | mod unsubscribe_group; 19 | 20 | pub use self::attachment::*; 21 | pub use self::bcc_settings::*; 22 | pub use self::bypass_list_management::*; 23 | pub use self::click_tracking::*; 24 | pub use self::content::*; 25 | pub use self::email_address::*; 26 | pub use self::footer_settings::*; 27 | pub use self::google_analytics::*; 28 | pub use self::mail_settings::*; 29 | pub use self::message_builder::*; 30 | pub use self::open_tracking::*; 31 | pub use self::personalization::*; 32 | pub use self::sandbox_mode::*; 33 | pub use self::spam_check::*; 34 | pub use self::subscription_tracking::*; 35 | pub use self::tracking_settings::*; 36 | pub use self::unsubscribe_group::*; 37 | -------------------------------------------------------------------------------- /azure-functions/src/send_grid/attachment.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | /// Represents an email attachment. 4 | #[derive(Debug, Default, Clone, Serialize, Deserialize)] 5 | pub struct Attachment { 6 | /// The Base64 encoded content of the attachment. 7 | pub content: String, 8 | /// The mime type of the attachment (e.g. "image/jpeg"). 9 | #[serde(rename = "type")] 10 | pub mime_type: String, 11 | /// The filename of the attachment. 12 | pub filename: String, 13 | /// The content-disposition of the attachment specifying how you would like the attachment to be displayed. 14 | /// 15 | /// Supported values are "attachment" or "inline". Defaults to "attachment". 16 | /// 17 | /// For example, "inline" results in the attached file being displayed automatically within the message, 18 | /// while "attachment" results in the attached file requiring some action to be taken before it is displayed (e.g. opening or downloading the file). 19 | #[serde(skip_serializing_if = "Option::is_none")] 20 | pub disposition: Option, 21 | /// The attachment's unique content identifier. 22 | /// 23 | /// This is used when the disposition is set to "inline" and the attachment is an image, allowing the file to be displayed within the body of your email. 24 | /// 25 | /// ```html 26 | /// 27 | /// ``` 28 | #[serde(skip_serializing_if = "Option::is_none")] 29 | pub content_id: Option, 30 | } 31 | 32 | #[cfg(test)] 33 | mod tests { 34 | use super::*; 35 | use serde_json::to_string; 36 | 37 | #[test] 38 | fn it_serializes_to_json() { 39 | let json = to_string(&Attachment { 40 | content: "aGVsbG8gd29ybGQ=".to_owned(), 41 | mime_type: "text/plain".to_owned(), 42 | filename: "foo.txt".to_owned(), 43 | disposition: None, 44 | content_id: None, 45 | }) 46 | .unwrap(); 47 | 48 | assert_eq!( 49 | json, 50 | r#"{"content":"aGVsbG8gd29ybGQ=","type":"text/plain","filename":"foo.txt"}"# 51 | ); 52 | 53 | let json = to_string(&Attachment { 54 | content: "aGVsbG8gd29ybGQ=".to_owned(), 55 | mime_type: "text/plain".to_owned(), 56 | filename: "foo.txt".to_owned(), 57 | disposition: Some("inline".to_owned()), 58 | content_id: Some("123456".to_owned()), 59 | }) 60 | .unwrap(); 61 | 62 | assert_eq!( 63 | json, 64 | r#"{"content":"aGVsbG8gd29ybGQ=","type":"text/plain","filename":"foo.txt","disposition":"inline","content_id":"123456"}"# 65 | ); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /azure-functions/src/send_grid/bcc_settings.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | /// Represents bcc settings for an email message. 4 | /// 5 | /// The specified email will receive a blind carbon copy (BCC) of 6 | /// the very first personalization defined for an email message. 7 | #[derive(Debug, Default, Clone, Serialize, Deserialize)] 8 | pub struct BccSettings { 9 | /// The value indicating whether this setting is enabled. 10 | pub enable: bool, 11 | /// The email address that will receive the BCC. 12 | pub email: String, 13 | } 14 | 15 | #[cfg(test)] 16 | mod tests { 17 | use super::*; 18 | use serde_json::to_string; 19 | 20 | #[test] 21 | fn it_serializes_to_json() { 22 | let json = to_string(&BccSettings { 23 | enable: true, 24 | email: "foo@example.com".to_owned(), 25 | }) 26 | .unwrap(); 27 | 28 | assert_eq!(json, r#"{"enable":true,"email":"foo@example.com"}"#); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /azure-functions/src/send_grid/bypass_list_management.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | /// Represents the ability to bypass list management for an email message. 4 | /// 5 | /// This setting allows you to bypass all unsubscribe groups and suppressions to ensure 6 | /// that the email is delivered to every single recipient. This should only be used in 7 | /// emergencies when it is absolutely necessary that every recipient receives your email. 8 | #[derive(Debug, Default, Clone, Serialize, Deserialize)] 9 | pub struct BypassListManagement { 10 | /// The value indicating whether this setting is enabled. 11 | pub enable: bool, 12 | } 13 | 14 | #[cfg(test)] 15 | mod tests { 16 | use super::*; 17 | use serde_json::to_string; 18 | 19 | #[test] 20 | fn it_serializes_to_json() { 21 | let json = to_string(&BypassListManagement { enable: true }).unwrap(); 22 | 23 | assert_eq!(json, r#"{"enable":true}"#); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /azure-functions/src/send_grid/click_tracking.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | /// Represents the ability to track whether a recipient clicked a link in the email message. 4 | #[derive(Debug, Default, Clone, Serialize, Deserialize)] 5 | pub struct ClickTracking { 6 | /// The value indicating whether this setting is enabled. 7 | pub enable: bool, 8 | /// The value indicating if this setting should be included in the text/plain portion of the email message. 9 | pub enable_text: bool, 10 | } 11 | 12 | #[cfg(test)] 13 | mod tests { 14 | use super::*; 15 | use serde_json::to_string; 16 | 17 | #[test] 18 | fn it_serializes_to_json() { 19 | let json = to_string(&ClickTracking { 20 | enable: true, 21 | enable_text: false, 22 | }) 23 | .unwrap(); 24 | 25 | assert_eq!(json, r#"{"enable":true,"enable_text":false}"#); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /azure-functions/src/send_grid/content.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | /// Represents the content of an email message. 4 | /// 5 | /// You can include multiple mime types of content, but you must specify at least one. 6 | #[derive(Debug, Default, Clone, Serialize, Deserialize)] 7 | pub struct Content { 8 | /// The mime type of the content you are including in your email (e.g. "text/plain" or "text/html"). 9 | #[serde(rename = "type")] 10 | pub mime_type: String, 11 | /// The actual content of the specified mime type for the email message. 12 | pub value: String, 13 | } 14 | 15 | #[cfg(test)] 16 | mod tests { 17 | use super::*; 18 | use serde_json::to_string; 19 | 20 | #[test] 21 | fn it_serializes_to_json() { 22 | let json = to_string(&Content { 23 | mime_type: "text/plain".to_owned(), 24 | value: "hello world".to_owned(), 25 | }) 26 | .unwrap(); 27 | 28 | assert_eq!(json, r#"{"type":"text/plain","value":"hello world"}"#); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /azure-functions/src/send_grid/footer_settings.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | /// Represents footer settings for an email message. 4 | #[derive(Debug, Default, Clone, Serialize, Deserialize)] 5 | pub struct FooterSettings { 6 | /// The value indicating whether this setting is enabled. 7 | pub enable: bool, 8 | /// The plain text content of the footer. 9 | pub text: String, 10 | /// The HTML content of the footer. 11 | pub html: String, 12 | } 13 | 14 | #[cfg(test)] 15 | mod tests { 16 | use super::*; 17 | use serde_json::to_string; 18 | 19 | #[test] 20 | fn it_serializes_to_json() { 21 | let json = to_string(&FooterSettings { 22 | enable: true, 23 | text: "hello".to_owned(), 24 | html: "world".to_owned(), 25 | }) 26 | .unwrap(); 27 | 28 | assert_eq!(json, r#"{"enable":true,"text":"hello","html":"world"}"#); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /azure-functions/src/send_grid/google_analytics.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | /// Represents the ability to enable tracking provided by Google Analytics. 4 | #[derive(Debug, Default, Clone, Serialize, Deserialize)] 5 | pub struct GoogleAnalytics { 6 | /// The value indicating whether this setting is enabled. 7 | pub enable: bool, 8 | /// The name of the referrer source. 9 | #[serde(rename = "utm_source")] 10 | pub source: String, 11 | /// The name of the marketing medium. 12 | #[serde(rename = "utm_medium")] 13 | pub medium: String, 14 | /// The identification of any paid keywords. 15 | #[serde(rename = "utm_term")] 16 | pub term: String, 17 | /// The differentiation of your campaign from advertisements. 18 | #[serde(rename = "utm_content")] 19 | pub content: String, 20 | /// The name of the campaign. 21 | #[serde(rename = "utm_campaign")] 22 | pub campaign: String, 23 | } 24 | 25 | #[cfg(test)] 26 | mod tests { 27 | use super::*; 28 | use serde_json::to_string; 29 | 30 | #[test] 31 | fn it_serializes_to_json() { 32 | let json = to_string(&GoogleAnalytics { 33 | enable: true, 34 | source: "foo".to_owned(), 35 | medium: "bar".to_owned(), 36 | term: "baz".to_owned(), 37 | content: "jam".to_owned(), 38 | campaign: "cake".to_owned(), 39 | }) 40 | .unwrap(); 41 | 42 | assert_eq!( 43 | json, 44 | r#"{"enable":true,"utm_source":"foo","utm_medium":"bar","utm_term":"baz","utm_content":"jam","utm_campaign":"cake"}"# 45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /azure-functions/src/send_grid/mail_settings.rs: -------------------------------------------------------------------------------- 1 | use crate::send_grid::{BccSettings, BypassListManagement, FooterSettings, SandboxMode, SpamCheck}; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | /// Represents a collection of different mail settings that specify how an email message is handled. 5 | #[derive(Debug, Default, Clone, Serialize, Deserialize)] 6 | pub struct MailSettings { 7 | /// The BCC settings for the email message. 8 | #[serde(skip_serializing_if = "Option::is_none")] 9 | pub bcc: Option, 10 | /// The bypass list management settings for the email message. 11 | #[serde(skip_serializing_if = "Option::is_none")] 12 | pub bypass_list_management: Option, 13 | /// The footer settings for the email message. 14 | #[serde(skip_serializing_if = "Option::is_none")] 15 | pub footer: Option, 16 | /// The sandbox mode settings for the email message. 17 | #[serde(skip_serializing_if = "Option::is_none")] 18 | pub sandbox_mode: Option, 19 | /// The spam check settings for the email message. 20 | #[serde(skip_serializing_if = "Option::is_none")] 21 | pub spam_check: Option, 22 | } 23 | 24 | #[cfg(test)] 25 | mod tests { 26 | use super::*; 27 | use serde_json::to_string; 28 | 29 | #[test] 30 | fn it_serializes_to_json() { 31 | let json = to_string(&MailSettings { 32 | bcc: Some(BccSettings { 33 | enable: true, 34 | email: "foo@example.com".to_owned(), 35 | }), 36 | bypass_list_management: Some(BypassListManagement { enable: true }), 37 | footer: Some(FooterSettings { 38 | enable: true, 39 | text: "hello".to_owned(), 40 | html: "world".to_owned(), 41 | }), 42 | sandbox_mode: Some(SandboxMode { enable: true }), 43 | spam_check: Some(SpamCheck { 44 | enable: true, 45 | threshold: 7, 46 | post_to_url: "https://example.com".to_owned(), 47 | }), 48 | }) 49 | .unwrap(); 50 | 51 | assert_eq!( 52 | json, 53 | r#"{"bcc":{"enable":true,"email":"foo@example.com"},"bypass_list_management":{"enable":true},"footer":{"enable":true,"text":"hello","html":"world"},"sandbox_mode":{"enable":true},"spam_check":{"enable":true,"threshold":7,"post_to_url":"https://example.com"}}"# 54 | ); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /azure-functions/src/send_grid/open_tracking.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | /// Represents the ability to track whether a recipient opened an email message. 4 | /// 5 | /// Open tracking includes a single pixel image in the body of the content to determine when the email was opened. 6 | #[derive(Debug, Default, Clone, Serialize, Deserialize)] 7 | pub struct OpenTracking { 8 | /// The value indicating whether this setting is enabled. 9 | pub enable: bool, 10 | /// The substitution tag that can be used to control the desired location of the tracking pixel in the email message. 11 | /// 12 | /// The tag will be replaced by the open tracking pixel. 13 | #[serde(skip_serializing_if = "Option::is_none")] 14 | pub substitution_tag: Option, 15 | } 16 | 17 | #[cfg(test)] 18 | mod tests { 19 | use super::*; 20 | use serde_json::to_string; 21 | 22 | #[test] 23 | fn it_serializes_to_json() { 24 | let json = to_string(&OpenTracking { 25 | enable: true, 26 | substitution_tag: Some("foo".to_owned()), 27 | }) 28 | .unwrap(); 29 | 30 | assert_eq!(json, r#"{"enable":true,"substitution_tag":"foo"}"#); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /azure-functions/src/send_grid/sandbox_mode.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | /// Represents the ability to send a test email in a sandbox. 4 | /// 5 | /// This setting allows you to send a test email to ensure that your request body is valid 6 | /// and formatted correctly. 7 | /// 8 | /// For more information, please see the classroom documentation: 9 | /// https://sendgrid.com/docs/Classroom/Send/v3_Mail_Send/sandbox_mode.html 10 | #[derive(Debug, Default, Clone, Serialize, Deserialize)] 11 | pub struct SandboxMode { 12 | /// The value indicating whether this setting is enabled. 13 | pub enable: bool, 14 | } 15 | 16 | #[cfg(test)] 17 | mod tests { 18 | use super::*; 19 | use serde_json::to_string; 20 | 21 | #[test] 22 | fn it_serializes_to_json() { 23 | let json = to_string(&SandboxMode { enable: true }).unwrap(); 24 | 25 | assert_eq!(json, r#"{"enable":true}"#); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /azure-functions/src/send_grid/spam_check.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | /// Represents the ability to test the email message for spam content. 4 | #[derive(Debug, Default, Clone, Serialize, Deserialize)] 5 | pub struct SpamCheck { 6 | /// The value indicating whether this setting is enabled. 7 | pub enable: bool, 8 | /// The threshold used to determine if your content qualifies as spam on a scale from 1 to 10. 9 | /// 10 | /// A value of 10 is the most strict or most likely to be considered as spam. 11 | pub threshold: i32, 12 | /// The inbound post URL that you would like a copy of your email, along with the spam report, sent to. 13 | /// 14 | /// The URL must start with `http://` or `https://`. 15 | pub post_to_url: String, 16 | } 17 | 18 | #[cfg(test)] 19 | mod tests { 20 | use super::*; 21 | use serde_json::to_string; 22 | 23 | #[test] 24 | fn it_serializes_to_json() { 25 | let json = to_string(&SpamCheck { 26 | enable: true, 27 | threshold: 7, 28 | post_to_url: "https://example.com".to_owned(), 29 | }) 30 | .unwrap(); 31 | 32 | assert_eq!( 33 | json, 34 | r#"{"enable":true,"threshold":7,"post_to_url":"https://example.com"}"# 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /azure-functions/src/send_grid/subscription_tracking.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | /// Represents the ability to insert a subscription management link at the bottom of the text and html bodies of email messages. 4 | /// 5 | /// If you would like to specify the location of the link within your email, use `substitution_tag`. 6 | #[derive(Debug, Default, Clone, Serialize, Deserialize)] 7 | pub struct SubscriptionTracking { 8 | /// The value indicating whether this setting is enabled. 9 | pub enable: bool, 10 | /// The text to be appended to the email, with the subscription tracking link. 11 | pub text: String, 12 | /// The HTML to be appended to the email, with the subscription tracking link. 13 | pub html: String, 14 | /// The tag that will be replaced with the unsubscribe URL. 15 | #[serde(skip_serializing_if = "Option::is_none")] 16 | pub substitution_tag: Option, 17 | } 18 | 19 | #[cfg(test)] 20 | mod tests { 21 | use super::*; 22 | use serde_json::to_string; 23 | 24 | #[test] 25 | fn it_serializes_to_json() { 26 | let json = to_string(&SubscriptionTracking { 27 | enable: true, 28 | text: "foo".to_owned(), 29 | html: "bar".to_owned(), 30 | substitution_tag: Some("baz".to_owned()), 31 | }) 32 | .unwrap(); 33 | 34 | assert_eq!( 35 | json, 36 | r#"{"enable":true,"text":"foo","html":"bar","substitution_tag":"baz"}"# 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /azure-functions/src/send_grid/tracking_settings.rs: -------------------------------------------------------------------------------- 1 | use crate::send_grid::{ClickTracking, GoogleAnalytics, OpenTracking, SubscriptionTracking}; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | /// Represents a collection of different mail settings that specify how an email message is handled. 5 | #[derive(Debug, Default, Clone, Serialize, Deserialize)] 6 | pub struct TrackingSettings { 7 | /// The click tracking settings for the email message. 8 | #[serde(skip_serializing_if = "Option::is_none")] 9 | pub click_tracking: Option, 10 | /// The open tracking settings for the email message. 11 | #[serde(skip_serializing_if = "Option::is_none")] 12 | pub open_tracking: Option, 13 | /// The subscription tracking settings for the email message. 14 | #[serde(skip_serializing_if = "Option::is_none")] 15 | pub subscription_tracking: Option, 16 | /// The Google Analytics settings for the email message. 17 | #[serde(rename = "ganalytics", skip_serializing_if = "Option::is_none")] 18 | pub google_analytics: Option, 19 | } 20 | 21 | #[cfg(test)] 22 | mod tests { 23 | use super::*; 24 | use serde_json::to_string; 25 | 26 | #[test] 27 | fn it_serializes_to_json() { 28 | let json = to_string(&TrackingSettings { 29 | click_tracking: Some(ClickTracking { 30 | enable: true, 31 | enable_text: false, 32 | }), 33 | open_tracking: Some(OpenTracking { 34 | enable: true, 35 | substitution_tag: Some("foo".to_owned()), 36 | }), 37 | subscription_tracking: Some(SubscriptionTracking { 38 | enable: true, 39 | text: "foo".to_owned(), 40 | html: "bar".to_owned(), 41 | substitution_tag: Some("baz".to_owned()), 42 | }), 43 | google_analytics: Some(GoogleAnalytics { 44 | enable: true, 45 | source: "foo".to_owned(), 46 | medium: "bar".to_owned(), 47 | term: "baz".to_owned(), 48 | content: "jam".to_owned(), 49 | campaign: "cake".to_owned(), 50 | }), 51 | }) 52 | .unwrap(); 53 | 54 | assert_eq!( 55 | json, 56 | r#"{"click_tracking":{"enable":true,"enable_text":false},"open_tracking":{"enable":true,"substitution_tag":"foo"},"subscription_tracking":{"enable":true,"text":"foo","html":"bar","substitution_tag":"baz"},"ganalytics":{"enable":true,"utm_source":"foo","utm_medium":"bar","utm_term":"baz","utm_content":"jam","utm_campaign":"cake"}}"# 57 | ); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /azure-functions/src/send_grid/unsubscribe_group.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | /// Represents an unsubscribe group associated with an email message that specifies how to handle unsubscribes. 4 | #[derive(Debug, Default, Clone, Serialize, Deserialize)] 5 | pub struct UnsubscribeGroup { 6 | /// The unsubscribe group id associated with the email message. 7 | pub group_id: i32, 8 | /// The list containing the unsubscribe groups that you would like to be displayed on the unsubscribe preferences page. 9 | /// See https://sendgrid.com/docs/User_Guide/Suppressions/recipient_subscription_preferences.html 10 | #[serde(skip_serializing_if = "Vec::is_empty")] 11 | pub groups_to_display: Vec, 12 | } 13 | 14 | #[cfg(test)] 15 | mod tests { 16 | use super::*; 17 | use serde_json::to_string; 18 | 19 | #[test] 20 | fn it_serializes_to_json() { 21 | let json = to_string(&UnsubscribeGroup { 22 | group_id: 12345, 23 | groups_to_display: Vec::new(), 24 | }) 25 | .unwrap(); 26 | 27 | assert_eq!(json, r#"{"group_id":12345}"#); 28 | 29 | let json = to_string(&UnsubscribeGroup { 30 | group_id: 12345, 31 | groups_to_display: vec![1, 2, 3, 4, 5], 32 | }) 33 | .unwrap(); 34 | 35 | assert_eq!( 36 | json, 37 | r#"{"group_id":12345,"groups_to_display":[1,2,3,4,5]}"# 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /azure-functions/src/signalr.rs: -------------------------------------------------------------------------------- 1 | //! Module for SignalR types. 2 | use serde::{Deserialize, Serialize}; 3 | 4 | /// Represents an action to take on a SignalR group. 5 | #[derive(Debug, Clone, Serialize, Deserialize)] 6 | #[serde(rename_all = "camelCase")] 7 | pub enum GroupAction { 8 | /// Adds a user to a group. 9 | Add, 10 | /// Removes a user from a group. 11 | Remove, 12 | } 13 | -------------------------------------------------------------------------------- /azure-functions/src/timer.rs: -------------------------------------------------------------------------------- 1 | //! Module for timer types. 2 | mod schedule_status; 3 | 4 | pub use self::schedule_status::*; 5 | -------------------------------------------------------------------------------- /azure-functions/src/timer/schedule_status.rs: -------------------------------------------------------------------------------- 1 | use crate::util::deserialize_datetime; 2 | use chrono::{DateTime, Utc}; 3 | use serde::Deserialize; 4 | 5 | /// Represents a timer binding schedule status. 6 | #[derive(Debug, Deserialize)] 7 | #[serde(rename_all = "PascalCase")] 8 | pub struct ScheduleStatus { 9 | /// The last recorded schedule occurrence. 10 | #[serde(deserialize_with = "deserialize_datetime")] 11 | pub last: DateTime, 12 | /// The expected next schedule occurrence. 13 | #[serde(deserialize_with = "deserialize_datetime")] 14 | pub next: DateTime, 15 | /// The last time the timer record was updated. 16 | /// 17 | /// This is used to re-calculate `next` with the current schedule after a host restart. 18 | #[serde(deserialize_with = "deserialize_datetime")] 19 | pub last_updated: DateTime, 20 | } 21 | 22 | #[cfg(test)] 23 | mod tests { 24 | use super::*; 25 | use serde_json::from_str; 26 | 27 | #[test] 28 | fn it_deserializes_from_json() { 29 | const JSON: &'static str = r#"{"Last":"0001-01-01T00:00:00","Next":"2018-07-24T23:24:00-07:00","LastUpdated":"2018-07-28T02:00:32+00:00"}"#; 30 | 31 | let status: ScheduleStatus = 32 | from_str(JSON).expect("failed to parse schedule status JSON data"); 33 | assert_eq!(status.last.to_rfc3339(), "0001-01-01T00:00:00+00:00"); 34 | assert_eq!(status.next.to_rfc3339(), "2018-07-25T06:24:00+00:00"); 35 | assert_eq!( 36 | status.last_updated.to_rfc3339(), 37 | "2018-07-28T02:00:32+00:00" 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /docker/build-image/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rust:slim 2 | LABEL MAINTAINER Peter Huene 3 | 4 | ENV PATH "$PATH:/root/.cargo/bin" 5 | ENV DOTNET_SKIP_FIRST_TIME_EXPERIENCE true 6 | ENV DOTNET_CLI_TELEMETRY_OPTOUT true 7 | 8 | RUN apt-get update \ 9 | && apt-get upgrade -y \ 10 | && apt-get install -y wget unzip apt-transport-https gnupg \ 11 | && wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.asc.gpg \ 12 | && mv microsoft.asc.gpg /etc/apt/trusted.gpg.d/ \ 13 | && wget -q https://packages.microsoft.com/config/debian/9/prod.list \ 14 | && mv prod.list /etc/apt/sources.list.d/microsoft-prod.list \ 15 | && apt-get update \ 16 | && wget https://github.com/google/protobuf/releases/download/v3.6.1/protoc-3.6.1-linux-x86_64.zip \ 17 | && unzip protoc-3.6.1-linux-x86_64.zip -d /usr \ 18 | && rm protoc-3.6.1-linux-x86_64.zip \ 19 | && apt-get install -y dotnet-sdk-2.2 \ 20 | && apt-get remove -y --purge wget unzip apt-transport-https gnupg \ 21 | && apt-get autoremove -y \ 22 | && apt-get clean \ 23 | && rm -rf /usr/share/dotnet/sdk/NuGetFallbackFolder/* 24 | 25 | WORKDIR /root 26 | 27 | CMD ["/bin/true"] 28 | -------------------------------------------------------------------------------- /examples/blob/.dockerignore: -------------------------------------------------------------------------------- 1 | target/ 2 | Cargo.lock 3 | .vscode/ 4 | .git/ 5 | -------------------------------------------------------------------------------- /examples/blob/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "lldb", 6 | "request": "attach", 7 | "name": "Debug", 8 | "windows": { 9 | "program": "blob-example.exe" 10 | }, 11 | "program": "blob-example", 12 | "preLaunchTask": "Launch Azure Functions Application", 13 | "postDebugTask": "Terminate Azure Functions Application" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /examples/blob/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "Launch Azure Functions Application", 6 | "type": "shell", 7 | "command": "cargo", 8 | "args": [ 9 | "func", 10 | "run" 11 | ], 12 | "presentation": { 13 | "reveal": "always", 14 | "clear": true, 15 | "focus": true 16 | }, 17 | "problemMatcher": [ 18 | { 19 | "owner": "azureFunctions", 20 | "pattern": [ 21 | { 22 | "regexp": "\\b\\B", 23 | "file": 1, 24 | "location": 2, 25 | "message": 3 26 | } 27 | ], 28 | "background": { 29 | "activeOnStart": true, 30 | "beginsPattern": "^Azure Functions Core Tools", 31 | "endsPattern": "^Application started." 32 | } 33 | } 34 | ], 35 | "isBackground": true 36 | }, 37 | { 38 | "label": "Terminate Azure Functions Application", 39 | "type": "process", 40 | "command":"${command:workbench.action.tasks.terminate}", 41 | "args": [ 42 | "Launch Azure Functions Application" 43 | ] 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /examples/blob/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "blob-example" 3 | version = "0.1.0" 4 | authors = ["Peter Huene "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | azure-functions = { path = "../../azure-functions" } 9 | log = "0.4.8" 10 | 11 | [features] 12 | unstable = ["azure-functions/unstable"] 13 | -------------------------------------------------------------------------------- /examples/blob/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile-upstream:experimental 2 | 3 | FROM peterhuene/azure-functions-rs-build:0.11.0 AS build-image 4 | 5 | WORKDIR /src 6 | COPY . /src 7 | 8 | # Run with mounted cache 9 | RUN --mount=type=cache,target=/src/target \ 10 | --mount=type=cache,target=/usr/local/cargo/git \ 11 | --mount=type=cache,target=/usr/local/cargo/registry \ 12 | ["cargo", "run", "--release", "--", "init", "--script-root", "/home/site/wwwroot", "--sync-extensions"] 13 | 14 | FROM mcr.microsoft.com/azure-functions/base:2.0 as runtime-image 15 | 16 | FROM mcr.microsoft.com/dotnet/core/runtime-deps:2.2 17 | 18 | ENV AzureWebJobsScriptRoot=/home/site/wwwroot \ 19 | HOME=/home \ 20 | FUNCTIONS_WORKER_RUNTIME=Rust \ 21 | languageWorkers__workersDirectory=/home/site/wwwroot/workers 22 | 23 | # Copy the Azure Functions host from the runtime image 24 | COPY --from=runtime-image [ "/azure-functions-host", "/azure-functions-host" ] 25 | 26 | # Copy the script root contents from the build image 27 | COPY --from=build-image ["/home/site/wwwroot", "/home/site/wwwroot"] 28 | 29 | WORKDIR /home/site/wwwroot 30 | CMD [ "/azure-functions-host/Microsoft.Azure.WebJobs.Script.WebHost" ] 31 | -------------------------------------------------------------------------------- /examples/blob/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "logging": { 4 | "logLevel": { 5 | "default": "Information" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/blob/local.settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "IsEncrypted": false, 3 | "Values": { 4 | "FUNCTIONS_WORKER_RUNTIME": "Rust", 5 | "languageWorkers:workersDirectory": "workers" 6 | }, 7 | "ConnectionStrings": { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/blob/src/functions/blob_watcher.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{bindings::BlobTrigger, func}; 2 | 3 | #[func] 4 | #[binding(name = "trigger", path = "watching/{name}")] 5 | pub fn blob_watcher(trigger: BlobTrigger) { 6 | log::info!( 7 | "A blob was created at '{}' with contents: {:?}.", 8 | trigger.path, 9 | trigger.blob 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /examples/blob/src/functions/copy_blob.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{ 2 | bindings::{Blob, HttpRequest, HttpResponse}, 3 | func, 4 | }; 5 | 6 | #[func] 7 | #[binding(name = "_req", route = "copy/blob/{container}/{name}")] 8 | #[binding(name = "blob", path = "{container}/{name}")] 9 | #[binding(name = "output1", path = "{container}/{name}.copy")] 10 | pub fn copy_blob(_req: HttpRequest, blob: Blob) -> (HttpResponse, Blob) { 11 | ("blob has been copied.".into(), blob) 12 | } 13 | -------------------------------------------------------------------------------- /examples/blob/src/functions/create_blob.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{ 2 | bindings::{Blob, HttpRequest, HttpResponse}, 3 | func, 4 | http::Status, 5 | }; 6 | 7 | #[func] 8 | #[binding(name = "req", route = "create/blob/{container}/{name}")] 9 | #[binding(name = "output1", path = "{container}/{name}")] 10 | pub fn create_blob(req: HttpRequest) -> (HttpResponse, Blob) { 11 | ( 12 | HttpResponse::build() 13 | .status(Status::Created) 14 | .body("blob has been created.") 15 | .finish(), 16 | req.body().as_bytes().into(), 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /examples/blob/src/functions/mod.rs: -------------------------------------------------------------------------------- 1 | // WARNING: This file is regenerated by the `cargo func new` command. 2 | 3 | mod blob_watcher; 4 | mod copy_blob; 5 | mod create_blob; 6 | mod print_blob; 7 | 8 | // Export the Azure Functions here. 9 | azure_functions::export! { 10 | blob_watcher::blob_watcher, 11 | copy_blob::copy_blob, 12 | create_blob::create_blob, 13 | print_blob::print_blob, 14 | } 15 | -------------------------------------------------------------------------------- /examples/blob/src/functions/print_blob.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{ 2 | bindings::{Blob, HttpRequest, HttpResponse}, 3 | func, 4 | }; 5 | 6 | #[func] 7 | #[binding(name = "_req", route = "print/blob/{container}/{path}")] 8 | #[binding(name = "blob", path = "{container}/{path}")] 9 | pub fn print_blob(_req: HttpRequest, blob: Blob) -> HttpResponse { 10 | blob.into() 11 | } 12 | -------------------------------------------------------------------------------- /examples/blob/src/main.rs: -------------------------------------------------------------------------------- 1 | mod functions; 2 | 3 | pub fn main() { 4 | azure_functions::worker_main(std::env::args(), functions::EXPORTS); 5 | } 6 | -------------------------------------------------------------------------------- /examples/cosmosdb/.dockerignore: -------------------------------------------------------------------------------- 1 | target/ 2 | Cargo.lock 3 | .vscode/ 4 | .git/ 5 | -------------------------------------------------------------------------------- /examples/cosmosdb/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "lldb", 6 | "request": "attach", 7 | "name": "Debug", 8 | "windows": { 9 | "program": "cosmosdb-example.exe" 10 | }, 11 | "program": "cosmosdb-example", 12 | "preLaunchTask": "Launch Azure Functions Application", 13 | "postDebugTask": "Terminate Azure Functions Application" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /examples/cosmosdb/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "Launch Azure Functions Application", 6 | "type": "shell", 7 | "command": "cargo", 8 | "args": [ 9 | "func", 10 | "run" 11 | ], 12 | "presentation": { 13 | "reveal": "always", 14 | "clear": true, 15 | "focus": true 16 | }, 17 | "problemMatcher": [ 18 | { 19 | "owner": "azureFunctions", 20 | "pattern": [ 21 | { 22 | "regexp": "\\b\\B", 23 | "file": 1, 24 | "location": 2, 25 | "message": 3 26 | } 27 | ], 28 | "background": { 29 | "activeOnStart": true, 30 | "beginsPattern": "^Azure Functions Core Tools", 31 | "endsPattern": "^Application started." 32 | } 33 | } 34 | ], 35 | "isBackground": true 36 | }, 37 | { 38 | "label": "Terminate Azure Functions Application", 39 | "type": "process", 40 | "command":"${command:workbench.action.tasks.terminate}", 41 | "args": [ 42 | "Launch Azure Functions Application" 43 | ] 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /examples/cosmosdb/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cosmosdb-example" 3 | version = "0.1.0" 4 | authors = ["Peter Huene "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | azure-functions = { path = "../../azure-functions" } 9 | log = "0.4.8" 10 | serde_json = "1.0.41" 11 | 12 | [features] 13 | unstable = ["azure-functions/unstable"] 14 | -------------------------------------------------------------------------------- /examples/cosmosdb/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile-upstream:experimental 2 | 3 | FROM peterhuene/azure-functions-rs-build:0.11.0 AS build-image 4 | 5 | WORKDIR /src 6 | COPY . /src 7 | 8 | # Run with mounted cache 9 | RUN --mount=type=cache,target=/src/target \ 10 | --mount=type=cache,target=/usr/local/cargo/git \ 11 | --mount=type=cache,target=/usr/local/cargo/registry \ 12 | ["cargo", "run", "--release", "--", "init", "--script-root", "/home/site/wwwroot", "--sync-extensions"] 13 | 14 | FROM mcr.microsoft.com/azure-functions/base:2.0 as runtime-image 15 | 16 | FROM mcr.microsoft.com/dotnet/core/runtime-deps:2.2 17 | 18 | ENV AzureWebJobsScriptRoot=/home/site/wwwroot \ 19 | HOME=/home \ 20 | FUNCTIONS_WORKER_RUNTIME=Rust \ 21 | languageWorkers__workersDirectory=/home/site/wwwroot/workers 22 | 23 | # Copy the Azure Functions host from the runtime image 24 | COPY --from=runtime-image [ "/azure-functions-host", "/azure-functions-host" ] 25 | 26 | # Copy the script root contents from the build image 27 | COPY --from=build-image ["/home/site/wwwroot", "/home/site/wwwroot"] 28 | 29 | WORKDIR /home/site/wwwroot 30 | CMD [ "/azure-functions-host/Microsoft.Azure.WebJobs.Script.WebHost" ] 31 | -------------------------------------------------------------------------------- /examples/cosmosdb/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "logging": { 4 | "logLevel": { 5 | "default": "Information" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/cosmosdb/local.settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "IsEncrypted": false, 3 | "Values": { 4 | "FUNCTIONS_WORKER_RUNTIME": "Rust", 5 | "languageWorkers:workersDirectory": "workers" 6 | }, 7 | "ConnectionStrings": { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/cosmosdb/src/functions/create_document.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{ 2 | bindings::{CosmosDbDocument, HttpRequest, HttpResponse}, 3 | func, 4 | }; 5 | use serde_json::json; 6 | 7 | #[func] 8 | #[binding(name = "req", route = "create/{id}")] 9 | #[binding( 10 | name = "output1", 11 | connection = "connection", 12 | database_name = "exampledb", 13 | collection_name = "documents", 14 | create_collection = true 15 | )] 16 | pub fn create_document(req: HttpRequest) -> (HttpResponse, CosmosDbDocument) { 17 | ( 18 | "Document was created.".into(), 19 | json!({ 20 | "id": req.route_params().get("id").unwrap(), 21 | "name": req.query_params().get("name").map_or("stranger", |x| x) 22 | }) 23 | .into(), 24 | ) 25 | } 26 | -------------------------------------------------------------------------------- /examples/cosmosdb/src/functions/log_documents.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{bindings::CosmosDbTrigger, func}; 2 | use log::info; 3 | 4 | #[func] 5 | #[binding( 6 | name = "trigger", 7 | connection = "connection", 8 | database_name = "exampledb", 9 | collection_name = "documents", 10 | create_lease_collection = true 11 | )] 12 | pub fn log_documents(trigger: CosmosDbTrigger) { 13 | for document in trigger.documents { 14 | info!("{:#?}", document); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/cosmosdb/src/functions/mod.rs: -------------------------------------------------------------------------------- 1 | // WARNING: This file is regenerated by the `cargo func new` command. 2 | 3 | mod create_document; 4 | mod log_documents; 5 | mod query_documents; 6 | mod read_document; 7 | 8 | // Export the Azure Functions here. 9 | azure_functions::export! { 10 | create_document::create_document, 11 | log_documents::log_documents, 12 | query_documents::query_documents, 13 | read_document::read_document, 14 | } 15 | -------------------------------------------------------------------------------- /examples/cosmosdb/src/functions/query_documents.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{ 2 | bindings::{CosmosDbDocument, HttpRequest, HttpResponse}, 3 | func, 4 | }; 5 | 6 | #[func] 7 | #[binding(name = "_req", route = "query/{name}")] 8 | #[binding( 9 | name = "documents", 10 | connection = "connection", 11 | database_name = "exampledb", 12 | collection_name = "documents", 13 | sql_query = "select * from documents d where contains(d.name, {name})", 14 | create_collection = true 15 | )] 16 | pub fn query_documents(_req: HttpRequest, documents: Vec) -> HttpResponse { 17 | documents.into() 18 | } 19 | -------------------------------------------------------------------------------- /examples/cosmosdb/src/functions/read_document.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{ 2 | bindings::{CosmosDbDocument, HttpRequest, HttpResponse}, 3 | func, 4 | }; 5 | 6 | #[func] 7 | #[binding(name = "req", route = "read/{id}")] 8 | #[binding( 9 | name = "document", 10 | connection = "connection", 11 | database_name = "exampledb", 12 | collection_name = "documents", 13 | id = "{id}", 14 | partition_key = "{id}" 15 | )] 16 | pub fn read_document(req: HttpRequest, document: CosmosDbDocument) -> HttpResponse { 17 | if document.is_null() { 18 | format!( 19 | "Document with id '{}' does not exist.", 20 | req.route_params().get("id").unwrap() 21 | ) 22 | .into() 23 | } else { 24 | document.into() 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/cosmosdb/src/main.rs: -------------------------------------------------------------------------------- 1 | mod functions; 2 | 3 | fn main() { 4 | azure_functions::worker_main(std::env::args(), functions::EXPORTS); 5 | } 6 | -------------------------------------------------------------------------------- /examples/durable-functions/.dockerignore: -------------------------------------------------------------------------------- 1 | target/ 2 | Cargo.lock 3 | .vscode/ 4 | .git/ 5 | -------------------------------------------------------------------------------- /examples/durable-functions/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "lldb", 6 | "request": "attach", 7 | "name": "Debug", 8 | "windows": { 9 | "program": "durable-functions-example.exe" 10 | }, 11 | "program": "durable-functions-example", 12 | "preLaunchTask": "Launch Azure Functions Application", 13 | "postDebugTask": "Terminate Azure Functions Application" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /examples/durable-functions/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "Launch Azure Functions Application", 6 | "type": "shell", 7 | "command": "cargo", 8 | "args": [ 9 | "func", 10 | "run", 11 | "--", 12 | "--features", 13 | "unstable" 14 | ], 15 | "presentation": { 16 | "reveal": "always", 17 | "clear": true, 18 | "focus": true 19 | }, 20 | "problemMatcher": [ 21 | { 22 | "owner": "azureFunctions", 23 | "pattern": [ 24 | { 25 | "regexp": "\\b\\B", 26 | "file": 1, 27 | "location": 2, 28 | "message": 3 29 | } 30 | ], 31 | "background": { 32 | "activeOnStart": true, 33 | "beginsPattern": "^Azure Functions Core Tools", 34 | "endsPattern": "^Application started." 35 | } 36 | } 37 | ], 38 | "isBackground": true 39 | }, 40 | { 41 | "label": "Terminate Azure Functions Application", 42 | "type": "process", 43 | "command":"${command:workbench.action.tasks.terminate}", 44 | "args": [ 45 | "Launch Azure Functions Application" 46 | ] 47 | } 48 | ] 49 | } 50 | -------------------------------------------------------------------------------- /examples/durable-functions/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "durable-functions-example" 3 | version = "0.1.0" 4 | authors = ["Peter Huene "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | azure-functions = { path = "../../azure-functions" } 9 | log = "0.4.6" 10 | futures-preview = "0.3.0-alpha.19" 11 | serde_json = "1.0.40" 12 | chrono = { version = "0.4.9" } 13 | 14 | [features] 15 | unstable = ["azure-functions/unstable"] 16 | -------------------------------------------------------------------------------- /examples/durable-functions/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile-upstream:experimental 2 | 3 | FROM peterhuene/azure-functions-rs-build:0.11.0 AS build-image 4 | 5 | WORKDIR /src 6 | COPY . /src 7 | 8 | # Run with mounted cache 9 | RUN --mount=type=cache,target=/src/target \ 10 | --mount=type=cache,target=/usr/local/cargo/git \ 11 | --mount=type=cache,target=/usr/local/cargo/registry \ 12 | ["cargo", "run", "--release", "--", "init", "--script-root", "/home/site/wwwroot", "--sync-extensions"] 13 | 14 | FROM mcr.microsoft.com/azure-functions/base:2.0 as runtime-image 15 | 16 | FROM mcr.microsoft.com/dotnet/core/runtime-deps:2.2 17 | 18 | ENV AzureWebJobsScriptRoot=/home/site/wwwroot \ 19 | HOME=/home \ 20 | FUNCTIONS_WORKER_RUNTIME=Rust \ 21 | languageWorkers__workersDirectory=/home/site/wwwroot/workers 22 | 23 | # Copy the Azure Functions host from the runtime image 24 | COPY --from=runtime-image [ "/azure-functions-host", "/azure-functions-host" ] 25 | 26 | # Copy the script root contents from the build image 27 | COPY --from=build-image ["/home/site/wwwroot", "/home/site/wwwroot"] 28 | 29 | WORKDIR /home/site/wwwroot 30 | CMD [ "/azure-functions-host/Microsoft.Azure.WebJobs.Script.WebHost" ] 31 | -------------------------------------------------------------------------------- /examples/durable-functions/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "logging": { 4 | "logLevel": { 5 | "default": "Information" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/durable-functions/local.settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "IsEncrypted": false, 3 | "Values": { 4 | "FUNCTIONS_WORKER_RUNTIME": "Rust", 5 | "languageWorkers:workersDirectory": "workers" 6 | }, 7 | "ConnectionStrings": {} 8 | } 9 | -------------------------------------------------------------------------------- /examples/durable-functions/src/functions/call_join.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{bindings::DurableOrchestrationContext, func}; 2 | use log::{error, info}; 3 | use serde_json::Value; 4 | 5 | #[func] 6 | pub async fn call_join(context: DurableOrchestrationContext) { 7 | match context 8 | .call_sub_orchestrator("join", None, Value::Null) 9 | .await 10 | { 11 | Ok(output) => info!("The output of the sub orchestration was: {}", output), 12 | Err(e) => error!("The sub orchestration failed: {}", e), 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /examples/durable-functions/src/functions/join.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{bindings::DurableOrchestrationContext, durable::OrchestrationOutput, func}; 2 | use log::{error, info}; 3 | use serde_json::Value; 4 | 5 | #[func] 6 | pub async fn join(context: DurableOrchestrationContext) -> OrchestrationOutput { 7 | if !context.is_replaying() { 8 | info!("Orchestration started at {}.", context.current_time()); 9 | } 10 | 11 | let activities = vec![ 12 | context.call_activity("say_hello", "Tokyo"), 13 | context.call_activity("say_hello", "London"), 14 | context.call_activity("say_hello", "Seattle"), 15 | ]; 16 | 17 | if !context.is_replaying() { 18 | info!("Joining all activities."); 19 | } 20 | 21 | context.set_custom_status("Waiting for all activities to complete."); 22 | 23 | let result: Value = context 24 | .join_all(activities) 25 | .await 26 | .into_iter() 27 | .filter_map(|r| { 28 | r.map(Some).unwrap_or_else(|e| { 29 | error!("Activity failed: {}", e); 30 | None 31 | }) 32 | }) 33 | .collect::>() 34 | .into(); 35 | 36 | if !context.is_replaying() { 37 | info!( 38 | "Orchestration completed at {} with result: {}.", 39 | context.current_time(), 40 | result 41 | ); 42 | } 43 | 44 | context.set_custom_status("All activities have completed."); 45 | 46 | result.into() 47 | } 48 | -------------------------------------------------------------------------------- /examples/durable-functions/src/functions/looping.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{bindings::DurableOrchestrationContext, func}; 2 | use log::info; 3 | 4 | #[func] 5 | pub async fn looping(context: DurableOrchestrationContext) { 6 | let value = context.input.as_i64().expect("expected a number for input"); 7 | 8 | if !context.is_replaying() { 9 | info!("The current value is: {}.", value); 10 | } 11 | 12 | if value < 10 { 13 | context.continue_as_new(value + 1, true); 14 | return; 15 | } 16 | 17 | if !context.is_replaying() { 18 | info!("Loop has completed."); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/durable-functions/src/functions/mod.rs: -------------------------------------------------------------------------------- 1 | // WARNING: This file is regenerated by the `cargo func new` command. 2 | 3 | mod call_join; 4 | mod join; 5 | mod looping; 6 | mod raise_event; 7 | mod say_hello; 8 | mod select; 9 | mod start; 10 | mod start_looping; 11 | mod timer; 12 | mod wait_for_event; 13 | 14 | // Export the Azure Functions here. 15 | azure_functions::export! { 16 | call_join::call_join, 17 | join::join, 18 | looping::looping, 19 | raise_event::raise_event, 20 | say_hello::say_hello, 21 | select::select, 22 | start::start, 23 | start_looping::start_looping, 24 | timer::timer, 25 | wait_for_event::wait_for_event, 26 | } 27 | -------------------------------------------------------------------------------- /examples/durable-functions/src/functions/raise_event.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{ 2 | bindings::{DurableOrchestrationClient, HttpRequest, HttpResponse}, 3 | func, 4 | }; 5 | 6 | #[func] 7 | pub async fn raise_event(req: HttpRequest, client: DurableOrchestrationClient) -> HttpResponse { 8 | let id = req 9 | .query_params() 10 | .get("id") 11 | .expect("expected a 'id' parameter"); 12 | 13 | let name = req 14 | .query_params() 15 | .get("name") 16 | .expect("expected a 'name' parameter"); 17 | 18 | let value = req 19 | .query_params() 20 | .get("value") 21 | .expect("expected a 'value' parameter") 22 | .clone(); 23 | 24 | match client.raise_event(id, name, value).await { 25 | Ok(_) => format!("Raised event named '{}'.", name).into(), 26 | Err(e) => format!("Failed to raise event named '{}': {}", name, e).into(), 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /examples/durable-functions/src/functions/say_hello.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{bindings::DurableActivityContext, durable::ActivityOutput, func}; 2 | 3 | #[func] 4 | pub fn say_hello(context: DurableActivityContext) -> ActivityOutput { 5 | format!( 6 | "Hello {}!", 7 | context.input.as_str().expect("expected a string input") 8 | ) 9 | .into() 10 | } 11 | -------------------------------------------------------------------------------- /examples/durable-functions/src/functions/select.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{bindings::DurableOrchestrationContext, func}; 2 | use log::{error, info}; 3 | 4 | #[func] 5 | pub async fn select(context: DurableOrchestrationContext) { 6 | if !context.is_replaying() { 7 | info!("Orchestration started at {}.", context.current_time()); 8 | } 9 | 10 | let mut activities = vec![ 11 | context.call_activity("say_hello", "Jakarta"), 12 | context.call_activity("say_hello", "Portland"), 13 | context.call_activity("say_hello", "New York"), 14 | ]; 15 | 16 | if !context.is_replaying() { 17 | info!("Selecting all activities."); 18 | } 19 | 20 | let mut completed = 0; 21 | 22 | while !activities.is_empty() { 23 | context.set_custom_status(format!( 24 | "Waiting on {} remaining activities.", 25 | activities.len() 26 | )); 27 | 28 | let (r, _, remaining) = context.select_all(activities).await; 29 | 30 | completed += 1; 31 | 32 | if !context.is_replaying() { 33 | match r { 34 | Ok(output) => info!("Activity #{} completed with output: {}", completed, output), 35 | Err(e) => error!("Activity #{} failed: {}", completed, e), 36 | }; 37 | } 38 | 39 | activities = remaining; 40 | } 41 | 42 | context.set_custom_status("All activities have completed."); 43 | 44 | if !context.is_replaying() { 45 | info!("Orchestration completed at {}.", context.current_time(),); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /examples/durable-functions/src/functions/start.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{ 2 | bindings::{DurableOrchestrationClient, HttpRequest, HttpResponse}, 3 | func, 4 | }; 5 | use serde_json::Value; 6 | 7 | #[func] 8 | pub async fn start(req: HttpRequest, client: DurableOrchestrationClient) -> HttpResponse { 9 | match client 10 | .start_new( 11 | req.query_params() 12 | .get("function") 13 | .expect("expected a function parameter"), 14 | None, 15 | Value::Null, 16 | ) 17 | .await 18 | { 19 | Ok(data) => data.into(), 20 | Err(e) => format!("Failed to start orchestration: {}", e).into(), 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /examples/durable-functions/src/functions/start_looping.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{ 2 | bindings::{DurableOrchestrationClient, HttpRequest, HttpResponse}, 3 | func, 4 | }; 5 | 6 | #[func] 7 | pub async fn start_looping(_req: HttpRequest, client: DurableOrchestrationClient) -> HttpResponse { 8 | match client.start_new("looping", None, 0).await { 9 | Ok(data) => data.into(), 10 | Err(e) => format!("Failed to start orchestration: {}", e).into(), 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /examples/durable-functions/src/functions/timer.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{bindings::DurableOrchestrationContext, func}; 2 | use chrono::Duration; 3 | use log::info; 4 | 5 | #[func] 6 | pub async fn timer(context: DurableOrchestrationContext) { 7 | if !context.is_replaying() { 8 | info!("Waiting 5 seconds."); 9 | } 10 | 11 | context 12 | .create_timer(context.current_time() + Duration::seconds(5)) 13 | .await; 14 | 15 | if !context.is_replaying() { 16 | info!("Timer has fired."); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/durable-functions/src/functions/wait_for_event.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{bindings::DurableOrchestrationContext, durable::OrchestrationOutput, func}; 2 | use log::info; 3 | 4 | #[func] 5 | pub async fn wait_for_event(context: DurableOrchestrationContext) -> OrchestrationOutput { 6 | if !context.is_replaying() { 7 | info!("Waiting for event named 'event'."); 8 | } 9 | 10 | let v = context.wait_for_event("event").await.unwrap(); 11 | 12 | if !context.is_replaying() { 13 | info!("Event was raised with value: {}.", v.as_str().unwrap()); 14 | } 15 | 16 | v.into() 17 | } 18 | -------------------------------------------------------------------------------- /examples/durable-functions/src/main.rs: -------------------------------------------------------------------------------- 1 | mod functions; 2 | 3 | fn main() { 4 | azure_functions::worker_main(std::env::args(), functions::EXPORTS); 5 | } 6 | -------------------------------------------------------------------------------- /examples/event-grid/.dockerignore: -------------------------------------------------------------------------------- 1 | target/ 2 | Cargo.lock 3 | .vscode/ 4 | .git/ 5 | -------------------------------------------------------------------------------- /examples/event-grid/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "lldb", 6 | "request": "attach", 7 | "name": "Debug", 8 | "windows": { 9 | "program": "event-grid-example.exe" 10 | }, 11 | "program": "event-grid-example", 12 | "preLaunchTask": "Launch Azure Functions Application", 13 | "postDebugTask": "Terminate Azure Functions Application" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /examples/event-grid/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "Launch Azure Functions Application", 6 | "type": "shell", 7 | "command": "cargo", 8 | "args": [ 9 | "func", 10 | "run" 11 | ], 12 | "presentation": { 13 | "reveal": "always", 14 | "clear": true, 15 | "focus": true 16 | }, 17 | "problemMatcher": [ 18 | { 19 | "owner": "azureFunctions", 20 | "pattern": [ 21 | { 22 | "regexp": "\\b\\B", 23 | "file": 1, 24 | "location": 2, 25 | "message": 3 26 | } 27 | ], 28 | "background": { 29 | "activeOnStart": true, 30 | "beginsPattern": "^Azure Functions Core Tools", 31 | "endsPattern": "^Application started." 32 | } 33 | } 34 | ], 35 | "isBackground": true 36 | }, 37 | { 38 | "label": "Terminate Azure Functions Application", 39 | "type": "process", 40 | "command":"${command:workbench.action.tasks.terminate}", 41 | "args": [ 42 | "Launch Azure Functions Application" 43 | ] 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /examples/event-grid/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "event-grid-example" 3 | version = "0.1.0" 4 | authors = ["Peter Huene "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | azure-functions = { path = "../../azure-functions" } 9 | log = "0.4.8" 10 | 11 | [features] 12 | unstable = ["azure-functions/unstable"] 13 | -------------------------------------------------------------------------------- /examples/event-grid/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile-upstream:experimental 2 | 3 | FROM peterhuene/azure-functions-rs-build:0.11.0 AS build-image 4 | 5 | WORKDIR /src 6 | COPY . /src 7 | 8 | # Run with mounted cache 9 | RUN --mount=type=cache,target=/src/target \ 10 | --mount=type=cache,target=/usr/local/cargo/git \ 11 | --mount=type=cache,target=/usr/local/cargo/registry \ 12 | ["cargo", "run", "--release", "--", "init", "--script-root", "/home/site/wwwroot", "--sync-extensions"] 13 | 14 | FROM mcr.microsoft.com/azure-functions/base:2.0 as runtime-image 15 | 16 | FROM mcr.microsoft.com/dotnet/core/runtime-deps:2.2 17 | 18 | ENV AzureWebJobsScriptRoot=/home/site/wwwroot \ 19 | HOME=/home \ 20 | FUNCTIONS_WORKER_RUNTIME=Rust \ 21 | languageWorkers__workersDirectory=/home/site/wwwroot/workers 22 | 23 | # Copy the Azure Functions host from the runtime image 24 | COPY --from=runtime-image [ "/azure-functions-host", "/azure-functions-host" ] 25 | 26 | # Copy the script root contents from the build image 27 | COPY --from=build-image ["/home/site/wwwroot", "/home/site/wwwroot"] 28 | 29 | WORKDIR /home/site/wwwroot 30 | CMD [ "/azure-functions-host/Microsoft.Azure.WebJobs.Script.WebHost" ] 31 | -------------------------------------------------------------------------------- /examples/event-grid/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "logging": { 4 | "logLevel": { 5 | "default": "Information" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/event-grid/local.settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "IsEncrypted": false, 3 | "Values": { 4 | "FUNCTIONS_WORKER_RUNTIME": "Rust", 5 | "languageWorkers:workersDirectory": "workers" 6 | }, 7 | "ConnectionStrings": { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/event-grid/src/functions/log_event.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{bindings::EventGridEvent, func}; 2 | 3 | #[func] 4 | pub fn log_event(event: EventGridEvent) { 5 | log::info!("Event Data: {}", event.data); 6 | } 7 | -------------------------------------------------------------------------------- /examples/event-grid/src/functions/mod.rs: -------------------------------------------------------------------------------- 1 | // WARNING: This file is regenerated by the `cargo func new` command. 2 | 3 | mod log_event; 4 | 5 | // Export the Azure Functions here. 6 | azure_functions::export! { 7 | log_event::log_event 8 | } 9 | -------------------------------------------------------------------------------- /examples/event-grid/src/main.rs: -------------------------------------------------------------------------------- 1 | mod functions; 2 | 3 | fn main() { 4 | azure_functions::worker_main(std::env::args(), functions::EXPORTS); 5 | } 6 | -------------------------------------------------------------------------------- /examples/event-hub/.dockerignore: -------------------------------------------------------------------------------- 1 | target/ 2 | Cargo.lock 3 | .vscode/ 4 | .git/ 5 | -------------------------------------------------------------------------------- /examples/event-hub/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "lldb", 6 | "request": "attach", 7 | "name": "Debug", 8 | "windows": { 9 | "program": "event-hub-example.exe" 10 | }, 11 | "program": "event-hub-example", 12 | "preLaunchTask": "Launch Azure Functions Application", 13 | "postDebugTask": "Terminate Azure Functions Application" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /examples/event-hub/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "Launch Azure Functions Application", 6 | "type": "shell", 7 | "command": "cargo", 8 | "args": [ 9 | "func", 10 | "run" 11 | ], 12 | "presentation": { 13 | "reveal": "always", 14 | "clear": true, 15 | "focus": true 16 | }, 17 | "problemMatcher": [ 18 | { 19 | "owner": "azureFunctions", 20 | "pattern": [ 21 | { 22 | "regexp": "\\b\\B", 23 | "file": 1, 24 | "location": 2, 25 | "message": 3 26 | } 27 | ], 28 | "background": { 29 | "activeOnStart": true, 30 | "beginsPattern": "^Azure Functions Core Tools", 31 | "endsPattern": "^Application started." 32 | } 33 | } 34 | ], 35 | "isBackground": true 36 | }, 37 | { 38 | "label": "Terminate Azure Functions Application", 39 | "type": "process", 40 | "command":"${command:workbench.action.tasks.terminate}", 41 | "args": [ 42 | "Launch Azure Functions Application" 43 | ] 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /examples/event-hub/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "event-hub-example" 3 | version = "0.1.0" 4 | authors = ["Peter Huene "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | azure-functions = { path = "../../azure-functions" } 9 | log = "0.4.8" 10 | 11 | [features] 12 | unstable = ["azure-functions/unstable"] 13 | -------------------------------------------------------------------------------- /examples/event-hub/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile-upstream:experimental 2 | 3 | FROM peterhuene/azure-functions-rs-build:0.11.0 AS build-image 4 | 5 | WORKDIR /src 6 | COPY . /src 7 | 8 | # Run with mounted cache 9 | RUN --mount=type=cache,target=/src/target \ 10 | --mount=type=cache,target=/usr/local/cargo/git \ 11 | --mount=type=cache,target=/usr/local/cargo/registry \ 12 | ["cargo", "run", "--release", "--", "init", "--script-root", "/home/site/wwwroot", "--sync-extensions"] 13 | 14 | FROM mcr.microsoft.com/azure-functions/base:2.0 as runtime-image 15 | 16 | FROM mcr.microsoft.com/dotnet/core/runtime-deps:2.2 17 | 18 | ENV AzureWebJobsScriptRoot=/home/site/wwwroot \ 19 | HOME=/home \ 20 | FUNCTIONS_WORKER_RUNTIME=Rust \ 21 | languageWorkers__workersDirectory=/home/site/wwwroot/workers 22 | 23 | # Copy the Azure Functions host from the runtime image 24 | COPY --from=runtime-image [ "/azure-functions-host", "/azure-functions-host" ] 25 | 26 | # Copy the script root contents from the build image 27 | COPY --from=build-image ["/home/site/wwwroot", "/home/site/wwwroot"] 28 | 29 | WORKDIR /home/site/wwwroot 30 | CMD [ "/azure-functions-host/Microsoft.Azure.WebJobs.Script.WebHost" ] 31 | -------------------------------------------------------------------------------- /examples/event-hub/README.md: -------------------------------------------------------------------------------- 1 | # Example Event Hub Azure Functions 2 | 3 | This project is an example of using Event Hub with Azure Functions for Rust. 4 | 5 | ## Example function implementations 6 | 7 | An example Event Hub triggered Azure Function that runs when a new message is posted 8 | to the `example` Event Hub: 9 | 10 | ```rust 11 | use azure_functions::{ 12 | bindings::EventHubTrigger, 13 | func, 14 | }; 15 | 16 | #[func] 17 | #[binding(name = "trigger", connection = "connection", event_hub_name = "example")] 18 | pub fn log_event(trigger: EventHubTrigger) { 19 | log::info!("Event hub message: {}", trigger.message.as_str().unwrap()); 20 | } 21 | ``` 22 | 23 | An example HTTP-triggered Azure Function that outputs a message to the `example` Event Hub: 24 | 25 | ```rust 26 | use azure_functions::{ 27 | bindings::{HttpRequest, HttpResponse, EventHubMessage}, 28 | func, 29 | }; 30 | 31 | #[func] 32 | #[binding(name = "output1", connection = "connection", event_hub_name = "example")] 33 | pub fn create_event(_req: HttpRequest) -> (HttpResponse, EventHubMessage) { 34 | ( 35 | "Created Event Hub message.".into(), 36 | "Hello from Rust!".into() 37 | ) 38 | } 39 | ``` 40 | 41 | # Running the example locally 42 | 43 | Because this example relies on Azure Storage to function, the `AzureWebJobsStorage` setting must be set to a connection string that the Azure Functions Host will use for the default 44 | storage connection. 45 | 46 | Add a setting for `AzureWebJobsStorage` into `local.settings.json`: 47 | 48 | ``` 49 | $ func settings add AzureWebJobsStorage 50 | ``` 51 | 52 | Additionally, this example uses a connection setting named `connection` for the Event Hubs connection string, so add that setting: 53 | 54 | ``` 55 | $ func settings add connection 56 | ``` 57 | 58 | You may encrypt `local.settings.json`, if desired: 59 | 60 | ``` 61 | $ func settings encrypt 62 | ``` 63 | 64 | This example expects an `example` Event Hub to exist so ensure one has been created in the Azure Portal. 65 | 66 | Finally, start the Azure Functions application: 67 | 68 | ``` 69 | $ cargo func run 70 | ``` 71 | 72 | # Invoking the functions 73 | 74 | ## Invoke the `create_event` function 75 | 76 | This function is designed to trigger the `log_event` function by posting a message for the monitored Event Hub. 77 | 78 | Simply use `curl` to invoke the `create_event` function: 79 | 80 | ``` 81 | $ curl http://localhost:8080/api/create_event 82 | ``` 83 | 84 | With any luck, something like the following should be logged by the Azure Functions Host: 85 | 86 | ``` 87 | Event hub message: Hello from Rust! 88 | ``` -------------------------------------------------------------------------------- /examples/event-hub/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "logging": { 4 | "logLevel": { 5 | "default": "Information" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/event-hub/local.settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "IsEncrypted": false, 3 | "Values": { 4 | "FUNCTIONS_WORKER_RUNTIME": "Rust", 5 | "languageWorkers:workersDirectory": "workers" 6 | }, 7 | "ConnectionStrings": { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/event-hub/src/functions/create_event.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{ 2 | bindings::{EventHubMessage, HttpRequest, HttpResponse}, 3 | func, 4 | }; 5 | 6 | #[func] 7 | #[binding( 8 | name = "output1", 9 | connection = "connection", 10 | event_hub_name = "example" 11 | )] 12 | pub fn create_event(_req: HttpRequest) -> (HttpResponse, EventHubMessage) { 13 | ( 14 | "Created Event Hub message.".into(), 15 | "Hello from Rust!".into(), 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /examples/event-hub/src/functions/log_event.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{bindings::EventHubTrigger, func}; 2 | 3 | #[func] 4 | #[binding( 5 | name = "trigger", 6 | connection = "connection", 7 | event_hub_name = "example" 8 | )] 9 | pub fn log_event(trigger: EventHubTrigger) { 10 | log::info!("Event hub message: {}", trigger.message.as_str().unwrap()); 11 | } 12 | -------------------------------------------------------------------------------- /examples/event-hub/src/functions/mod.rs: -------------------------------------------------------------------------------- 1 | // WARNING: This file is regenerated by the `cargo func new` command. 2 | 3 | mod create_event; 4 | mod log_event; 5 | 6 | // Export the Azure Functions here. 7 | azure_functions::export! { 8 | create_event::create_event, 9 | log_event::log_event, 10 | } 11 | -------------------------------------------------------------------------------- /examples/event-hub/src/main.rs: -------------------------------------------------------------------------------- 1 | mod functions; 2 | 3 | fn main() { 4 | azure_functions::worker_main(std::env::args(), functions::EXPORTS); 5 | } 6 | -------------------------------------------------------------------------------- /examples/generic/.dockerignore: -------------------------------------------------------------------------------- 1 | target/ 2 | Cargo.lock 3 | .vscode/ 4 | .git/ 5 | -------------------------------------------------------------------------------- /examples/generic/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "lldb", 6 | "request": "attach", 7 | "name": "Debug", 8 | "windows": { 9 | "program": "generic-example.exe" 10 | }, 11 | "program": "generic-example", 12 | "preLaunchTask": "Launch Azure Functions Application", 13 | "postDebugTask": "Terminate Azure Functions Application" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /examples/generic/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "Launch Azure Functions Application", 6 | "type": "shell", 7 | "command": "cargo", 8 | "args": [ 9 | "func", 10 | "run" 11 | ], 12 | "presentation": { 13 | "reveal": "always", 14 | "clear": true, 15 | "focus": true 16 | }, 17 | "problemMatcher": [ 18 | { 19 | "owner": "azureFunctions", 20 | "pattern": [ 21 | { 22 | "regexp": "\\b\\B", 23 | "file": 1, 24 | "location": 2, 25 | "message": 3 26 | } 27 | ], 28 | "background": { 29 | "activeOnStart": true, 30 | "beginsPattern": "^Azure Functions Core Tools", 31 | "endsPattern": "^Application started." 32 | } 33 | } 34 | ], 35 | "isBackground": true 36 | }, 37 | { 38 | "label": "Terminate Azure Functions Application", 39 | "type": "process", 40 | "command":"${command:workbench.action.tasks.terminate}", 41 | "args": [ 42 | "Launch Azure Functions Application" 43 | ] 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /examples/generic/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "generic-example" 3 | version = "0.1.0" 4 | authors = ["Peter Huene "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | azure-functions = { path = "../../azure-functions" } 9 | log = "0.4.8" 10 | serde_json = "1.0.41" 11 | 12 | [features] 13 | unstable = ["azure-functions/unstable"] 14 | -------------------------------------------------------------------------------- /examples/generic/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile-upstream:experimental 2 | 3 | FROM peterhuene/azure-functions-rs-build:0.11.0 AS build-image 4 | 5 | WORKDIR /src 6 | COPY . /src 7 | 8 | # Run with mounted cache 9 | RUN --mount=type=cache,target=/src/target \ 10 | --mount=type=cache,target=/usr/local/cargo/git \ 11 | --mount=type=cache,target=/usr/local/cargo/registry \ 12 | ["cargo", "run", "--release", "--", "init", "--script-root", "/home/site/wwwroot", "--sync-extensions"] 13 | 14 | FROM mcr.microsoft.com/azure-functions/base:2.0 as runtime-image 15 | 16 | FROM mcr.microsoft.com/dotnet/core/runtime-deps:2.2 17 | 18 | ENV AzureWebJobsScriptRoot=/home/site/wwwroot \ 19 | HOME=/home \ 20 | FUNCTIONS_WORKER_RUNTIME=Rust \ 21 | languageWorkers__workersDirectory=/home/site/wwwroot/workers 22 | 23 | # Copy the Azure Functions host from the runtime image 24 | COPY --from=runtime-image [ "/azure-functions-host", "/azure-functions-host" ] 25 | 26 | # Copy the script root contents from the build image 27 | COPY --from=build-image ["/home/site/wwwroot", "/home/site/wwwroot"] 28 | 29 | WORKDIR /home/site/wwwroot 30 | CMD [ "/azure-functions-host/Microsoft.Azure.WebJobs.Script.WebHost" ] 31 | -------------------------------------------------------------------------------- /examples/generic/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "logging": { 4 | "logLevel": { 5 | "default": "Information" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/generic/local.settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "IsEncrypted": false, 3 | "Values": { 4 | "FUNCTIONS_WORKER_RUNTIME": "Rust", 5 | "languageWorkers:workersDirectory": "workers" 6 | }, 7 | "ConnectionStrings": { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/generic/src/functions/create_document.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{ 2 | bindings::{GenericOutput, HttpRequest, HttpResponse}, 3 | func, 4 | }; 5 | use serde_json::json; 6 | 7 | #[func] 8 | #[binding(name = "req", route = "create/{id}")] 9 | #[binding( 10 | type = "cosmosDB", 11 | name = "output1", 12 | connectionStringSetting = "connection", 13 | databaseName = "exampledb", 14 | collectionName = "documents", 15 | createIfNotExists = true 16 | )] 17 | pub fn create_document(req: HttpRequest) -> (HttpResponse, GenericOutput) { 18 | ( 19 | "Document was created.".into(), 20 | json!({ 21 | "id": req.route_params().get("id").unwrap(), 22 | "name": req.query_params().get("name").map_or("stranger", |x| x) 23 | }) 24 | .into(), 25 | ) 26 | } 27 | -------------------------------------------------------------------------------- /examples/generic/src/functions/log_documents.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{bindings::GenericTrigger, func, generic::Value}; 2 | use log::info; 3 | 4 | #[func] 5 | #[binding( 6 | type = "cosmosDBTrigger", 7 | name = "trigger", 8 | connectionStringSetting = "connection", 9 | databaseName = "exampledb", 10 | collectionName = "documents", 11 | createLeaseCollectionIfNotExists = true 12 | )] 13 | pub fn log_documents(trigger: GenericTrigger) { 14 | match trigger.data { 15 | Value::Json(v) => { 16 | info!("{}", v); 17 | } 18 | _ => panic!("expected JSON for Cosmos DB trigger data"), 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/generic/src/functions/mod.rs: -------------------------------------------------------------------------------- 1 | // WARNING: This file is regenerated by the `cargo func new` command. 2 | 3 | mod create_document; 4 | mod log_documents; 5 | mod query_documents; 6 | mod read_document; 7 | 8 | // Export the Azure Functions here. 9 | azure_functions::export! { 10 | create_document::create_document, 11 | log_documents::log_documents, 12 | query_documents::query_documents, 13 | read_document::read_document, 14 | } 15 | -------------------------------------------------------------------------------- /examples/generic/src/functions/query_documents.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{ 2 | bindings::{GenericInput, HttpRequest, HttpResponse}, 3 | func, 4 | }; 5 | 6 | #[func] 7 | #[binding(name = "_req", route = "query/{name}")] 8 | #[binding( 9 | type = "cosmosDB", 10 | name = "documents", 11 | connectionStringSetting = "connection", 12 | databaseName = "exampledb", 13 | collectionName = "documents", 14 | sqlQuery = "select * from documents d where contains(d.name, {name})", 15 | createIfNotExists = true 16 | )] 17 | pub fn query_documents(_req: HttpRequest, documents: GenericInput) -> HttpResponse { 18 | documents.into() 19 | } 20 | -------------------------------------------------------------------------------- /examples/generic/src/functions/read_document.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{ 2 | bindings::{GenericInput, HttpRequest, HttpResponse}, 3 | func, 4 | generic::Value, 5 | }; 6 | use serde_json::from_str; 7 | 8 | #[func] 9 | #[binding(name = "req", route = "read/{id}")] 10 | #[binding( 11 | type = "cosmosDB", 12 | name = "document", 13 | connectionStringSetting = "connection", 14 | databaseName = "exampledb", 15 | collectionName = "documents", 16 | id = "{id}", 17 | partitionKey = "{id}" 18 | )] 19 | pub fn read_document(req: HttpRequest, document: GenericInput) -> HttpResponse { 20 | match document.data { 21 | Value::String(s) => { 22 | let v: serde_json::Value = from_str(&s).expect("expected JSON data"); 23 | if v.is_null() { 24 | format!( 25 | "Document with id '{}' does not exist.", 26 | req.route_params().get("id").unwrap() 27 | ) 28 | .into() 29 | } else { 30 | v.into() 31 | } 32 | } 33 | _ => panic!("expected string for CosmosDB document data"), 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /examples/generic/src/main.rs: -------------------------------------------------------------------------------- 1 | mod functions; 2 | 3 | fn main() { 4 | azure_functions::worker_main_with_extensions( 5 | std::env::args(), 6 | functions::EXPORTS, 7 | &[("Microsoft.Azure.WebJobs.Extensions.CosmosDB", "3.0.3")], 8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /examples/http/.dockerignore: -------------------------------------------------------------------------------- 1 | target/ 2 | Cargo.lock 3 | .vscode/ 4 | .git/ 5 | -------------------------------------------------------------------------------- /examples/http/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "lldb", 6 | "request": "attach", 7 | "name": "Debug", 8 | "windows": { 9 | "program": "http-example.exe" 10 | }, 11 | "program": "http-example", 12 | "preLaunchTask": "Launch Azure Functions Application", 13 | "postDebugTask": "Terminate Azure Functions Application" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /examples/http/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "Launch Azure Functions Application", 6 | "type": "shell", 7 | "command": "cargo", 8 | "args": [ 9 | "func", 10 | "run" 11 | ], 12 | "presentation": { 13 | "reveal": "always", 14 | "clear": true, 15 | "focus": true 16 | }, 17 | "problemMatcher": [ 18 | { 19 | "owner": "azureFunctions", 20 | "pattern": [ 21 | { 22 | "regexp": "\\b\\B", 23 | "file": 1, 24 | "location": 2, 25 | "message": 3 26 | } 27 | ], 28 | "background": { 29 | "activeOnStart": true, 30 | "beginsPattern": "^Azure Functions Core Tools", 31 | "endsPattern": "^Application started." 32 | } 33 | } 34 | ], 35 | "isBackground": true 36 | }, 37 | { 38 | "label": "Terminate Azure Functions Application", 39 | "type": "process", 40 | "command":"${command:workbench.action.tasks.terminate}", 41 | "args": [ 42 | "Launch Azure Functions Application" 43 | ] 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /examples/http/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "http-example" 3 | version = "0.1.0" 4 | authors = ["Peter Huene "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | azure-functions = { path = "../../azure-functions" } 9 | log = "0.4.8" 10 | serde = { version = "1.0.102", features = ["derive"] } 11 | serde_json = "1.0.41" 12 | futures-preview = "0.3.0-alpha.19" 13 | 14 | [features] 15 | unstable = ["azure-functions/unstable"] 16 | -------------------------------------------------------------------------------- /examples/http/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile-upstream:experimental 2 | 3 | FROM peterhuene/azure-functions-rs-build:0.11.0 AS build-image 4 | 5 | WORKDIR /src 6 | COPY . /src 7 | 8 | # Run with mounted cache 9 | RUN --mount=type=cache,target=/src/target \ 10 | --mount=type=cache,target=/usr/local/cargo/git \ 11 | --mount=type=cache,target=/usr/local/cargo/registry \ 12 | ["cargo", "run", "--release", "--", "init", "--script-root", "/home/site/wwwroot", "--sync-extensions"] 13 | 14 | FROM mcr.microsoft.com/azure-functions/base:2.0 as runtime-image 15 | 16 | FROM mcr.microsoft.com/dotnet/core/runtime-deps:2.2 17 | 18 | ENV AzureWebJobsScriptRoot=/home/site/wwwroot \ 19 | HOME=/home \ 20 | FUNCTIONS_WORKER_RUNTIME=Rust \ 21 | languageWorkers__workersDirectory=/home/site/wwwroot/workers 22 | 23 | # Copy the Azure Functions host from the runtime image 24 | COPY --from=runtime-image [ "/azure-functions-host", "/azure-functions-host" ] 25 | 26 | # Copy the script root contents from the build image 27 | COPY --from=build-image ["/home/site/wwwroot", "/home/site/wwwroot"] 28 | 29 | WORKDIR /home/site/wwwroot 30 | CMD [ "/azure-functions-host/Microsoft.Azure.WebJobs.Script.WebHost" ] 31 | -------------------------------------------------------------------------------- /examples/http/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "logging": { 4 | "logLevel": { 5 | "default": "Information" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/http/local.settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "IsEncrypted": false, 3 | "Values": { 4 | "FUNCTIONS_WORKER_RUNTIME": "Rust", 5 | "languageWorkers:workersDirectory": "workers" 6 | }, 7 | "ConnectionStrings": { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/http/src/functions/greet.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{ 2 | bindings::{HttpRequest, HttpResponse}, 3 | func, 4 | }; 5 | 6 | #[func] 7 | pub fn greet(req: HttpRequest) -> HttpResponse { 8 | format!( 9 | "Hello from Rust, {}!\n", 10 | req.query_params().get("name").map_or("stranger", |x| x) 11 | ) 12 | .into() 13 | } 14 | -------------------------------------------------------------------------------- /examples/http/src/functions/greet_async.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{ 2 | bindings::{HttpRequest, HttpResponse}, 3 | func, 4 | }; 5 | use futures::future::ready; 6 | 7 | #[func] 8 | pub async fn greet_async(req: HttpRequest) -> HttpResponse { 9 | let response = format!( 10 | "Hello from Rust, {}!\n", 11 | req.query_params().get("name").map_or("stranger", |x| x) 12 | ); 13 | 14 | // Use ready().await to simply demonstrate the async/await feature 15 | ready(response).await.into() 16 | } 17 | -------------------------------------------------------------------------------- /examples/http/src/functions/greet_with_json.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{ 2 | bindings::{HttpRequest, HttpResponse}, 3 | func, 4 | http::Status, 5 | }; 6 | use serde::{Deserialize, Serialize}; 7 | use serde_json::to_value; 8 | 9 | #[derive(Deserialize)] 10 | struct Request { 11 | name: String, 12 | } 13 | 14 | #[derive(Serialize)] 15 | struct Response { 16 | message: String, 17 | } 18 | 19 | #[func] 20 | pub fn greet_with_json(req: HttpRequest) -> HttpResponse { 21 | if let Ok(request) = req.body().as_json::() { 22 | let response = Response { 23 | message: format!("Hello from Rust, {}!", request.name), 24 | }; 25 | return to_value(response).unwrap().into(); 26 | } 27 | 28 | HttpResponse::build() 29 | .status(Status::BadRequest) 30 | .body("Invalid JSON request.") 31 | .finish() 32 | } 33 | -------------------------------------------------------------------------------- /examples/http/src/functions/mod.rs: -------------------------------------------------------------------------------- 1 | // WARNING: This file is regenerated by the `cargo func new` command. 2 | 3 | mod greet; 4 | mod greet_async; 5 | mod greet_with_json; 6 | 7 | // Export the Azure Functions here. 8 | azure_functions::export! { 9 | greet::greet, 10 | greet_async::greet_async, 11 | greet_with_json::greet_with_json, 12 | } 13 | -------------------------------------------------------------------------------- /examples/http/src/main.rs: -------------------------------------------------------------------------------- 1 | mod functions; 2 | 3 | pub fn main() { 4 | azure_functions::worker_main(std::env::args(), functions::EXPORTS); 5 | } 6 | -------------------------------------------------------------------------------- /examples/queue/.dockerignore: -------------------------------------------------------------------------------- 1 | target/ 2 | Cargo.lock 3 | .vscode/ 4 | .git/ 5 | -------------------------------------------------------------------------------- /examples/queue/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "lldb", 6 | "request": "attach", 7 | "name": "Debug", 8 | "windows": { 9 | "program": "queue-example.exe" 10 | }, 11 | "program": "queue-example", 12 | "preLaunchTask": "Launch Azure Functions Application", 13 | "postDebugTask": "Terminate Azure Functions Application" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /examples/queue/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "Launch Azure Functions Application", 6 | "type": "shell", 7 | "command": "cargo", 8 | "args": [ 9 | "func", 10 | "run" 11 | ], 12 | "presentation": { 13 | "reveal": "always", 14 | "clear": true, 15 | "focus": true 16 | }, 17 | "problemMatcher": [ 18 | { 19 | "owner": "azureFunctions", 20 | "pattern": [ 21 | { 22 | "regexp": "\\b\\B", 23 | "file": 1, 24 | "location": 2, 25 | "message": 3 26 | } 27 | ], 28 | "background": { 29 | "activeOnStart": true, 30 | "beginsPattern": "^Azure Functions Core Tools", 31 | "endsPattern": "^Application started." 32 | } 33 | } 34 | ], 35 | "isBackground": true 36 | }, 37 | { 38 | "label": "Terminate Azure Functions Application", 39 | "type": "process", 40 | "command":"${command:workbench.action.tasks.terminate}", 41 | "args": [ 42 | "Launch Azure Functions Application" 43 | ] 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /examples/queue/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "queue-example" 3 | version = "0.1.0" 4 | authors = ["Peter Huene "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | azure-functions = { path = "../../azure-functions" } 9 | log = "0.4.8" 10 | 11 | [features] 12 | unstable = ["azure-functions/unstable"] 13 | -------------------------------------------------------------------------------- /examples/queue/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile-upstream:experimental 2 | 3 | FROM peterhuene/azure-functions-rs-build:0.11.0 AS build-image 4 | 5 | WORKDIR /src 6 | COPY . /src 7 | 8 | # Run with mounted cache 9 | RUN --mount=type=cache,target=/src/target \ 10 | --mount=type=cache,target=/usr/local/cargo/git \ 11 | --mount=type=cache,target=/usr/local/cargo/registry \ 12 | ["cargo", "run", "--release", "--", "init", "--script-root", "/home/site/wwwroot", "--sync-extensions"] 13 | 14 | FROM mcr.microsoft.com/azure-functions/base:2.0 as runtime-image 15 | 16 | FROM mcr.microsoft.com/dotnet/core/runtime-deps:2.2 17 | 18 | ENV AzureWebJobsScriptRoot=/home/site/wwwroot \ 19 | HOME=/home \ 20 | FUNCTIONS_WORKER_RUNTIME=Rust \ 21 | languageWorkers__workersDirectory=/home/site/wwwroot/workers 22 | 23 | # Copy the Azure Functions host from the runtime image 24 | COPY --from=runtime-image [ "/azure-functions-host", "/azure-functions-host" ] 25 | 26 | # Copy the script root contents from the build image 27 | COPY --from=build-image ["/home/site/wwwroot", "/home/site/wwwroot"] 28 | 29 | WORKDIR /home/site/wwwroot 30 | CMD [ "/azure-functions-host/Microsoft.Azure.WebJobs.Script.WebHost" ] 31 | -------------------------------------------------------------------------------- /examples/queue/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "logging": { 4 | "logLevel": { 5 | "default": "Information" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/queue/local.settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "IsEncrypted": false, 3 | "Values": { 4 | "FUNCTIONS_WORKER_RUNTIME": "Rust", 5 | "languageWorkers:workersDirectory": "workers" 6 | }, 7 | "ConnectionStrings": { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/queue/src/functions/mod.rs: -------------------------------------------------------------------------------- 1 | // WARNING: This file is regenerated by the `cargo func new` command. 2 | 3 | mod queue; 4 | mod queue_with_output; 5 | 6 | // Export the Azure Functions here. 7 | azure_functions::export! { 8 | queue::queue, 9 | queue_with_output::queue_with_output, 10 | } 11 | -------------------------------------------------------------------------------- /examples/queue/src/functions/queue.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{bindings::QueueTrigger, func}; 2 | 3 | #[func] 4 | #[binding(name = "trigger", queue_name = "test")] 5 | pub fn queue(trigger: QueueTrigger) { 6 | log::info!("Message: {}", trigger.message); 7 | } 8 | -------------------------------------------------------------------------------- /examples/queue/src/functions/queue_with_output.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{ 2 | bindings::{QueueMessage, QueueTrigger}, 3 | func, 4 | }; 5 | 6 | #[func] 7 | #[binding(name = "trigger", queue_name = "echo-in")] 8 | #[binding(name = "$return", queue_name = "echo-out")] 9 | pub fn queue_with_output(trigger: QueueTrigger) -> QueueMessage { 10 | log::info!("Message: {}", trigger.message); 11 | 12 | trigger.message 13 | } 14 | -------------------------------------------------------------------------------- /examples/queue/src/main.rs: -------------------------------------------------------------------------------- 1 | mod functions; 2 | 3 | pub fn main() { 4 | azure_functions::worker_main(std::env::args(), functions::EXPORTS); 5 | } 6 | -------------------------------------------------------------------------------- /examples/sendgrid/.dockerignore: -------------------------------------------------------------------------------- 1 | target/ 2 | Cargo.lock 3 | .vscode/ 4 | .git/ 5 | -------------------------------------------------------------------------------- /examples/sendgrid/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "lldb", 6 | "request": "attach", 7 | "name": "Debug", 8 | "windows": { 9 | "program": "sendgrid-example.exe" 10 | }, 11 | "program": "sendgrid-example", 12 | "preLaunchTask": "Launch Azure Functions Application", 13 | "postDebugTask": "Terminate Azure Functions Application" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /examples/sendgrid/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "Launch Azure Functions Application", 6 | "type": "shell", 7 | "command": "cargo", 8 | "args": [ 9 | "func", 10 | "run" 11 | ], 12 | "presentation": { 13 | "reveal": "always", 14 | "clear": true, 15 | "focus": true 16 | }, 17 | "problemMatcher": [ 18 | { 19 | "owner": "azureFunctions", 20 | "pattern": [ 21 | { 22 | "regexp": "\\b\\B", 23 | "file": 1, 24 | "location": 2, 25 | "message": 3 26 | } 27 | ], 28 | "background": { 29 | "activeOnStart": true, 30 | "beginsPattern": "^Azure Functions Core Tools", 31 | "endsPattern": "^Application started." 32 | } 33 | } 34 | ], 35 | "isBackground": true 36 | }, 37 | { 38 | "label": "Terminate Azure Functions Application", 39 | "type": "process", 40 | "command":"${command:workbench.action.tasks.terminate}", 41 | "args": [ 42 | "Launch Azure Functions Application" 43 | ] 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /examples/sendgrid/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sendgrid-example" 3 | version = "0.1.0" 4 | authors = ["Peter Huene "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | azure-functions = { path = "../../azure-functions" } 9 | 10 | [features] 11 | unstable = ["azure-functions/unstable"] 12 | -------------------------------------------------------------------------------- /examples/sendgrid/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile-upstream:experimental 2 | 3 | FROM peterhuene/azure-functions-rs-build:0.11.0 AS build-image 4 | 5 | WORKDIR /src 6 | COPY . /src 7 | 8 | # Run with mounted cache 9 | RUN --mount=type=cache,target=/src/target \ 10 | --mount=type=cache,target=/usr/local/cargo/git \ 11 | --mount=type=cache,target=/usr/local/cargo/registry \ 12 | ["cargo", "run", "--release", "--", "init", "--script-root", "/home/site/wwwroot", "--sync-extensions"] 13 | 14 | FROM mcr.microsoft.com/azure-functions/base:2.0 as runtime-image 15 | 16 | FROM mcr.microsoft.com/dotnet/core/runtime-deps:2.2 17 | 18 | ENV AzureWebJobsScriptRoot=/home/site/wwwroot \ 19 | HOME=/home \ 20 | FUNCTIONS_WORKER_RUNTIME=Rust \ 21 | languageWorkers__workersDirectory=/home/site/wwwroot/workers 22 | 23 | # Copy the Azure Functions host from the runtime image 24 | COPY --from=runtime-image [ "/azure-functions-host", "/azure-functions-host" ] 25 | 26 | # Copy the script root contents from the build image 27 | COPY --from=build-image ["/home/site/wwwroot", "/home/site/wwwroot"] 28 | 29 | WORKDIR /home/site/wwwroot 30 | CMD [ "/azure-functions-host/Microsoft.Azure.WebJobs.Script.WebHost" ] 31 | -------------------------------------------------------------------------------- /examples/sendgrid/README.md: -------------------------------------------------------------------------------- 1 | # Example SendGrid Azure Function 2 | 3 | This project is an example of using SendGrid with Azure Functions for Rust. 4 | 5 | ## Example function implementation 6 | 7 | An example HTTP-triggered Azure Function that outputs a SendGrid email message: 8 | 9 | ```rust 10 | use azure_functions::{ 11 | bindings::{HttpRequest, HttpResponse, SendGridMessage}, 12 | send_grid::MessageBuilder, 13 | func, 14 | }; 15 | 16 | #[func] 17 | #[binding(name = "output1", from = "azure.functions.for.rust@example.com")] 18 | pub fn send_email(req: HttpRequest) -> (HttpResponse, SendGridMessage) { 19 | let params = req.query_params(); 20 | 21 | ( 22 | "The email was sent.".into(), 23 | MessageBuilder::new() 24 | .to(params.get("to").unwrap().as_str()) 25 | .subject(params.get("subject").unwrap().as_str()) 26 | .content(params.get("content").unwrap().as_str()) 27 | .build(), 28 | ) 29 | } 30 | ``` 31 | 32 | # Running the example locally 33 | 34 | This example requires a [SendGrid](https://sendgrid.com/) account to run. 35 | 36 | First, sign up for a free account with SendGrid. The free trial account will 37 | allow you to send a limited number of email messages . With a paid account, 38 | you'll be able to send a higher volume of messages. 39 | 40 | You will need to [create a SendGrid API key](https://sendgrid.com/docs/ui/account-and-settings/api-keys/#creating-an-api-key) to use the example. 41 | 42 | Add the SendGrid API key as the `AzureWebJobsSendGridApiKey` setting to `local.settings.json`: 43 | 44 | ``` 45 | $ func settings add AzureWebJobsSendGridApiKey 46 | ``` 47 | 48 | You may encrypt `local.settings.json`, if desired: 49 | 50 | ``` 51 | $ func settings encrypt 52 | ``` 53 | 54 | Finally, start the Azure Functions application: 55 | 56 | ``` 57 | $ cargo func run 58 | ``` 59 | 60 | # Invoking the function 61 | 62 | ## Invoke the `send_email` function 63 | 64 | Simply use `curl` to invoke the `send_email` function with the desired phone number and message body: 65 | 66 | ``` 67 | $ curl "http://localhost:8080/api/send_email?to=$EMAIL&subject=test&content=hello%20world" 68 | ``` 69 | 70 | Where `$EMAIL` is replaced with the email address you would like to send the message to. 71 | 72 | With any luck, you should receive a "hello world" text message with the given message body. 73 | 74 | Because the "from" address of the email is an `example.com` address, it is likely that the email 75 | will end up in your spam folder, so make sure to check there in case it does not appear in your inbox. -------------------------------------------------------------------------------- /examples/sendgrid/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "logging": { 4 | "logLevel": { 5 | "default": "Information" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/sendgrid/local.settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "IsEncrypted": false, 3 | "Values": { 4 | "FUNCTIONS_WORKER_RUNTIME": "Rust", 5 | "languageWorkers:workersDirectory": "workers" 6 | }, 7 | "ConnectionStrings": {} 8 | } 9 | -------------------------------------------------------------------------------- /examples/sendgrid/src/functions/mod.rs: -------------------------------------------------------------------------------- 1 | // WARNING: This file is regenerated by the `cargo func new` command. 2 | 3 | mod send_email; 4 | 5 | // Export the Azure Functions here. 6 | azure_functions::export! { 7 | send_email::send_email, 8 | } 9 | -------------------------------------------------------------------------------- /examples/sendgrid/src/functions/send_email.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{ 2 | bindings::{HttpRequest, HttpResponse, SendGridMessage}, 3 | func, 4 | }; 5 | 6 | #[func] 7 | #[binding(name = "output1", from = "azure.functions.for.rust@example.com")] 8 | pub fn send_email(req: HttpRequest) -> (HttpResponse, SendGridMessage) { 9 | let params = req.query_params(); 10 | 11 | ( 12 | "The email was sent.".into(), 13 | SendGridMessage::build() 14 | .to(params.get("to").unwrap().as_str()) 15 | .subject(params.get("subject").unwrap().as_str()) 16 | .content(params.get("content").unwrap().as_str()) 17 | .finish(), 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /examples/sendgrid/src/main.rs: -------------------------------------------------------------------------------- 1 | mod functions; 2 | 3 | fn main() { 4 | azure_functions::worker_main(std::env::args(), functions::EXPORTS); 5 | } 6 | -------------------------------------------------------------------------------- /examples/service-bus/.dockerignore: -------------------------------------------------------------------------------- 1 | target/ 2 | Cargo.lock 3 | .vscode/ 4 | .git/ 5 | -------------------------------------------------------------------------------- /examples/service-bus/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "lldb", 6 | "request": "attach", 7 | "name": "Debug", 8 | "windows": { 9 | "program": "service-bus-example.exe" 10 | }, 11 | "program": "service-bus-example", 12 | "preLaunchTask": "Launch Azure Functions Application", 13 | "postDebugTask": "Terminate Azure Functions Application" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /examples/service-bus/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "Launch Azure Functions Application", 6 | "type": "shell", 7 | "command": "cargo", 8 | "args": [ 9 | "func", 10 | "run" 11 | ], 12 | "presentation": { 13 | "reveal": "always", 14 | "clear": true, 15 | "focus": true 16 | }, 17 | "problemMatcher": [ 18 | { 19 | "owner": "azureFunctions", 20 | "pattern": [ 21 | { 22 | "regexp": "\\b\\B", 23 | "file": 1, 24 | "location": 2, 25 | "message": 3 26 | } 27 | ], 28 | "background": { 29 | "activeOnStart": true, 30 | "beginsPattern": "^Azure Functions Core Tools", 31 | "endsPattern": "^Application started." 32 | } 33 | } 34 | ], 35 | "isBackground": true 36 | }, 37 | { 38 | "label": "Terminate Azure Functions Application", 39 | "type": "process", 40 | "command":"${command:workbench.action.tasks.terminate}", 41 | "args": [ 42 | "Launch Azure Functions Application" 43 | ] 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /examples/service-bus/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "service-bus-example" 3 | version = "0.1.0" 4 | authors = ["Peter Huene "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | azure-functions = { path = "../../azure-functions" } 9 | log = "0.4.8" 10 | 11 | [features] 12 | unstable = ["azure-functions/unstable"] 13 | -------------------------------------------------------------------------------- /examples/service-bus/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile-upstream:experimental 2 | 3 | FROM peterhuene/azure-functions-rs-build:0.11.0 AS build-image 4 | 5 | WORKDIR /src 6 | COPY . /src 7 | 8 | # Run with mounted cache 9 | RUN --mount=type=cache,target=/src/target \ 10 | --mount=type=cache,target=/usr/local/cargo/git \ 11 | --mount=type=cache,target=/usr/local/cargo/registry \ 12 | ["cargo", "run", "--release", "--", "init", "--script-root", "/home/site/wwwroot", "--sync-extensions"] 13 | 14 | FROM mcr.microsoft.com/azure-functions/base:2.0 as runtime-image 15 | 16 | FROM mcr.microsoft.com/dotnet/core/runtime-deps:2.2 17 | 18 | ENV AzureWebJobsScriptRoot=/home/site/wwwroot \ 19 | HOME=/home \ 20 | FUNCTIONS_WORKER_RUNTIME=Rust \ 21 | languageWorkers__workersDirectory=/home/site/wwwroot/workers 22 | 23 | # Copy the Azure Functions host from the runtime image 24 | COPY --from=runtime-image [ "/azure-functions-host", "/azure-functions-host" ] 25 | 26 | # Copy the script root contents from the build image 27 | COPY --from=build-image ["/home/site/wwwroot", "/home/site/wwwroot"] 28 | 29 | WORKDIR /home/site/wwwroot 30 | CMD [ "/azure-functions-host/Microsoft.Azure.WebJobs.Script.WebHost" ] 31 | -------------------------------------------------------------------------------- /examples/service-bus/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "logging": { 4 | "logLevel": { 5 | "default": "Information" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/service-bus/local.settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "IsEncrypted": false, 3 | "Values": { 4 | "FUNCTIONS_WORKER_RUNTIME": "Rust", 5 | "languageWorkers:workersDirectory": "workers" 6 | }, 7 | "ConnectionStrings": { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/service-bus/src/functions/create_queue_message.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{ 2 | bindings::{HttpRequest, ServiceBusMessage}, 3 | func, 4 | }; 5 | 6 | #[func] 7 | #[binding(name = "$return", queue_name = "example", connection = "connection")] 8 | pub fn create_queue_message(req: HttpRequest) -> ServiceBusMessage { 9 | format!( 10 | "Hello from Rust, {}!\n", 11 | req.query_params().get("name").map_or("stranger", |x| x) 12 | ) 13 | .into() 14 | } 15 | -------------------------------------------------------------------------------- /examples/service-bus/src/functions/create_topic_message.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{ 2 | bindings::{HttpRequest, ServiceBusMessage}, 3 | func, 4 | }; 5 | 6 | #[func] 7 | #[binding( 8 | name = "$return", 9 | topic_name = "mytopic", 10 | subscription_name = "mysubscription", 11 | connection = "connection" 12 | )] 13 | pub fn create_topic_message(req: HttpRequest) -> ServiceBusMessage { 14 | format!( 15 | "Hello from Rust, {}!\n", 16 | req.query_params().get("name").map_or("stranger", |x| x) 17 | ) 18 | .into() 19 | } 20 | -------------------------------------------------------------------------------- /examples/service-bus/src/functions/log_queue_message.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{bindings::ServiceBusTrigger, func}; 2 | 3 | #[func] 4 | #[binding(name = "trigger", queue_name = "example", connection = "connection")] 5 | pub fn log_queue_message(trigger: ServiceBusTrigger) { 6 | log::info!("{}", trigger.message.as_str().unwrap()); 7 | } 8 | -------------------------------------------------------------------------------- /examples/service-bus/src/functions/log_topic_message.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{bindings::ServiceBusTrigger, func}; 2 | 3 | #[func] 4 | #[binding( 5 | name = "trigger", 6 | topic_name = "mytopic", 7 | subscription_name = "mysubscription", 8 | connection = "connection" 9 | )] 10 | pub fn log_topic_message(trigger: ServiceBusTrigger) { 11 | log::info!("{}", trigger.message.as_str().unwrap()); 12 | } 13 | -------------------------------------------------------------------------------- /examples/service-bus/src/functions/mod.rs: -------------------------------------------------------------------------------- 1 | // WARNING: This file is regenerated by the `cargo func new` command. 2 | 3 | mod create_queue_message; 4 | mod create_topic_message; 5 | mod log_queue_message; 6 | mod log_topic_message; 7 | 8 | // Export the Azure Functions here. 9 | azure_functions::export! { 10 | create_queue_message::create_queue_message, 11 | create_topic_message::create_topic_message, 12 | log_queue_message::log_queue_message, 13 | log_topic_message::log_topic_message, 14 | } 15 | -------------------------------------------------------------------------------- /examples/service-bus/src/main.rs: -------------------------------------------------------------------------------- 1 | mod functions; 2 | 3 | fn main() { 4 | azure_functions::worker_main(std::env::args(), functions::EXPORTS); 5 | } 6 | -------------------------------------------------------------------------------- /examples/signalr/.dockerignore: -------------------------------------------------------------------------------- 1 | target/ 2 | Cargo.lock 3 | .vscode/ 4 | .git/ 5 | -------------------------------------------------------------------------------- /examples/signalr/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "lldb", 6 | "request": "attach", 7 | "name": "Debug", 8 | "windows": { 9 | "program": "signalr-example.exe" 10 | }, 11 | "program": "signalr-example", 12 | "preLaunchTask": "Launch Azure Functions Application", 13 | "postDebugTask": "Terminate Azure Functions Application" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /examples/signalr/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "Launch Azure Functions Application", 6 | "type": "shell", 7 | "command": "cargo", 8 | "args": [ 9 | "func", 10 | "run" 11 | ], 12 | "presentation": { 13 | "reveal": "always", 14 | "clear": true, 15 | "focus": true 16 | }, 17 | "problemMatcher": [ 18 | { 19 | "owner": "azureFunctions", 20 | "pattern": [ 21 | { 22 | "regexp": "\\b\\B", 23 | "file": 1, 24 | "location": 2, 25 | "message": 3 26 | } 27 | ], 28 | "background": { 29 | "activeOnStart": true, 30 | "beginsPattern": "^Azure Functions Core Tools", 31 | "endsPattern": "^Application started." 32 | } 33 | } 34 | ], 35 | "isBackground": true 36 | }, 37 | { 38 | "label": "Terminate Azure Functions Application", 39 | "type": "process", 40 | "command":"${command:workbench.action.tasks.terminate}", 41 | "args": [ 42 | "Launch Azure Functions Application" 43 | ] 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /examples/signalr/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "signalr-example" 3 | version = "0.1.0" 4 | authors = ["Peter Huene "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | azure-functions = { path = "../../azure-functions" } 9 | serde_json = "1.0.41" 10 | serde = {version = "1.0.102", features = ["derive"] } 11 | 12 | [features] 13 | unstable = ["azure-functions/unstable"] 14 | -------------------------------------------------------------------------------- /examples/signalr/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile-upstream:experimental 2 | 3 | FROM peterhuene/azure-functions-rs-build:0.11.0 AS build-image 4 | 5 | WORKDIR /src 6 | COPY . /src 7 | 8 | # Run with mounted cache 9 | RUN --mount=type=cache,target=/src/target \ 10 | --mount=type=cache,target=/usr/local/cargo/git \ 11 | --mount=type=cache,target=/usr/local/cargo/registry \ 12 | ["cargo", "run", "--release", "--", "init", "--script-root", "/home/site/wwwroot", "--sync-extensions"] 13 | 14 | FROM mcr.microsoft.com/azure-functions/base:2.0 as runtime-image 15 | 16 | FROM mcr.microsoft.com/dotnet/core/runtime-deps:2.2 17 | 18 | ENV AzureWebJobsScriptRoot=/home/site/wwwroot \ 19 | HOME=/home \ 20 | FUNCTIONS_WORKER_RUNTIME=Rust \ 21 | languageWorkers__workersDirectory=/home/site/wwwroot/workers 22 | 23 | # Copy the Azure Functions host from the runtime image 24 | COPY --from=runtime-image [ "/azure-functions-host", "/azure-functions-host" ] 25 | 26 | # Copy the script root contents from the build image 27 | COPY --from=build-image ["/home/site/wwwroot", "/home/site/wwwroot"] 28 | 29 | WORKDIR /home/site/wwwroot 30 | CMD [ "/azure-functions-host/Microsoft.Azure.WebJobs.Script.WebHost" ] 31 | -------------------------------------------------------------------------------- /examples/signalr/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "logging": { 4 | "logLevel": { 5 | "default": "Information" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/signalr/local.settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "IsEncrypted": false, 3 | "Values": { 4 | "FUNCTIONS_WORKER_RUNTIME": "Rust", 5 | "languageWorkers:workersDirectory": "workers" 6 | }, 7 | "ConnectionStrings": { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/signalr/src/functions/add_to_group.rs: -------------------------------------------------------------------------------- 1 | use crate::serialization::ChatMessage; 2 | use azure_functions::{ 3 | bindings::{HttpRequest, SignalRGroupAction}, 4 | func, 5 | signalr::GroupAction, 6 | }; 7 | 8 | #[func(name = "addToGroup")] 9 | #[binding(name = "req", auth_level = "anonymous", methods = "post")] 10 | #[binding(name = "$return", hub_name = "simplechat", connection = "connection")] 11 | pub fn add_to_group(req: HttpRequest) -> SignalRGroupAction { 12 | let message: ChatMessage = req 13 | .body() 14 | .as_json() 15 | .expect("failed to deserialize chat message"); 16 | SignalRGroupAction { 17 | user_id: message.recipient.unwrap(), 18 | group_name: message.group_name.unwrap(), 19 | action: GroupAction::Add, 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /examples/signalr/src/functions/mod.rs: -------------------------------------------------------------------------------- 1 | // WARNING: This file is regenerated by the `cargo func new` command. 2 | 3 | mod add_to_group; 4 | mod negotiate; 5 | mod remove_from_group; 6 | mod send_message; 7 | 8 | // Export the Azure Functions here. 9 | azure_functions::export! { 10 | add_to_group::add_to_group, 11 | negotiate::negotiate, 12 | remove_from_group::remove_from_group, 13 | send_message::send_message, 14 | } 15 | -------------------------------------------------------------------------------- /examples/signalr/src/functions/negotiate.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{ 2 | bindings::{HttpRequest, HttpResponse, SignalRConnectionInfo}, 3 | func, 4 | }; 5 | 6 | #[func] 7 | #[binding(name = "_req", auth_level = "anonymous")] 8 | #[binding( 9 | name = "info", 10 | hub_name = "simplechat", 11 | user_id = "{headers.x-ms-signalr-userid}", 12 | connection = "connection" 13 | )] 14 | pub fn negotiate(_req: HttpRequest, info: SignalRConnectionInfo) -> HttpResponse { 15 | info.into() 16 | } 17 | -------------------------------------------------------------------------------- /examples/signalr/src/functions/remove_from_group.rs: -------------------------------------------------------------------------------- 1 | use crate::serialization::ChatMessage; 2 | use azure_functions::{ 3 | bindings::{HttpRequest, SignalRGroupAction}, 4 | func, 5 | signalr::GroupAction, 6 | }; 7 | 8 | #[func(name = "removeFromGroup")] 9 | #[binding(name = "req", auth_level = "anonymous", methods = "post")] 10 | #[binding(name = "$return", hub_name = "simplechat", connection = "connection")] 11 | pub fn remove_from_group(req: HttpRequest) -> SignalRGroupAction { 12 | let message: ChatMessage = req 13 | .body() 14 | .as_json() 15 | .expect("failed to deserialize chat message"); 16 | SignalRGroupAction { 17 | user_id: message.recipient.unwrap(), 18 | group_name: message.group_name.unwrap(), 19 | action: GroupAction::Remove, 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /examples/signalr/src/functions/send_message.rs: -------------------------------------------------------------------------------- 1 | use crate::serialization::ChatMessage; 2 | use azure_functions::{ 3 | bindings::{HttpRequest, SignalRMessage}, 4 | func, 5 | }; 6 | use serde_json::to_value; 7 | 8 | #[func(name = "messages")] 9 | #[binding(name = "req", auth_level = "anonymous", methods = "post")] 10 | #[binding(name = "$return", hub_name = "simplechat", connection = "connection")] 11 | pub fn send_message(req: HttpRequest) -> SignalRMessage { 12 | let message: ChatMessage = req 13 | .body() 14 | .as_json() 15 | .expect("failed to deserialize chat message"); 16 | 17 | SignalRMessage { 18 | user_id: message.recipient.clone(), 19 | group_name: message.group_name.clone(), 20 | target: "newMessage".to_string(), 21 | arguments: vec![to_value(message).expect("failed to serialize chat message")], 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/signalr/src/main.rs: -------------------------------------------------------------------------------- 1 | mod functions; 2 | mod serialization; 3 | 4 | fn main() { 5 | azure_functions::worker_main(std::env::args(), functions::EXPORTS); 6 | } 7 | -------------------------------------------------------------------------------- /examples/signalr/src/serialization.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Serialize, Deserialize)] 4 | pub struct ChatMessage { 5 | pub sender: Option, 6 | pub text: Option, 7 | #[serde(rename = "groupname")] 8 | pub group_name: Option, 9 | pub recipient: Option, 10 | #[serde(rename = "isPrivate")] 11 | pub is_private: Option, 12 | } 13 | -------------------------------------------------------------------------------- /examples/table/.dockerignore: -------------------------------------------------------------------------------- 1 | target/ 2 | Cargo.lock 3 | .vscode/ 4 | .git/ 5 | -------------------------------------------------------------------------------- /examples/table/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "lldb", 6 | "request": "attach", 7 | "name": "Debug", 8 | "windows": { 9 | "program": "table-example.exe" 10 | }, 11 | "program": "table-example", 12 | "preLaunchTask": "Launch Azure Functions Application", 13 | "postDebugTask": "Terminate Azure Functions Application" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /examples/table/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "Launch Azure Functions Application", 6 | "type": "shell", 7 | "command": "cargo", 8 | "args": [ 9 | "func", 10 | "run" 11 | ], 12 | "presentation": { 13 | "reveal": "always", 14 | "clear": true, 15 | "focus": true 16 | }, 17 | "problemMatcher": [ 18 | { 19 | "owner": "azureFunctions", 20 | "pattern": [ 21 | { 22 | "regexp": "\\b\\B", 23 | "file": 1, 24 | "location": 2, 25 | "message": 3 26 | } 27 | ], 28 | "background": { 29 | "activeOnStart": true, 30 | "beginsPattern": "^Azure Functions Core Tools", 31 | "endsPattern": "^Application started." 32 | } 33 | } 34 | ], 35 | "isBackground": true 36 | }, 37 | { 38 | "label": "Terminate Azure Functions Application", 39 | "type": "process", 40 | "command":"${command:workbench.action.tasks.terminate}", 41 | "args": [ 42 | "Launch Azure Functions Application" 43 | ] 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /examples/table/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "table-example" 3 | version = "0.1.0" 4 | authors = ["Peter Huene "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | azure-functions = { path = "../../azure-functions" } 9 | log = "0.4.8" 10 | serde_json = "1.0.41" 11 | 12 | [features] 13 | unstable = ["azure-functions/unstable"] 14 | -------------------------------------------------------------------------------- /examples/table/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile-upstream:experimental 2 | 3 | FROM peterhuene/azure-functions-rs-build:0.11.0 AS build-image 4 | 5 | WORKDIR /src 6 | COPY . /src 7 | 8 | # Run with mounted cache 9 | RUN --mount=type=cache,target=/src/target \ 10 | --mount=type=cache,target=/usr/local/cargo/git \ 11 | --mount=type=cache,target=/usr/local/cargo/registry \ 12 | ["cargo", "run", "--release", "--", "init", "--script-root", "/home/site/wwwroot", "--sync-extensions"] 13 | 14 | FROM mcr.microsoft.com/azure-functions/base:2.0 as runtime-image 15 | 16 | FROM mcr.microsoft.com/dotnet/core/runtime-deps:2.2 17 | 18 | ENV AzureWebJobsScriptRoot=/home/site/wwwroot \ 19 | HOME=/home \ 20 | FUNCTIONS_WORKER_RUNTIME=Rust \ 21 | languageWorkers__workersDirectory=/home/site/wwwroot/workers 22 | 23 | # Copy the Azure Functions host from the runtime image 24 | COPY --from=runtime-image [ "/azure-functions-host", "/azure-functions-host" ] 25 | 26 | # Copy the script root contents from the build image 27 | COPY --from=build-image ["/home/site/wwwroot", "/home/site/wwwroot"] 28 | 29 | WORKDIR /home/site/wwwroot 30 | CMD [ "/azure-functions-host/Microsoft.Azure.WebJobs.Script.WebHost" ] 31 | -------------------------------------------------------------------------------- /examples/table/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "logging": { 4 | "logLevel": { 5 | "default": "Information" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/table/local.settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "IsEncrypted": false, 3 | "Values": { 4 | "FUNCTIONS_WORKER_RUNTIME": "Rust", 5 | "languageWorkers:workersDirectory": "workers" 6 | }, 7 | "ConnectionStrings": { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/table/src/functions/create_row.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{ 2 | bindings::{HttpRequest, Table}, 3 | func, 4 | }; 5 | use serde_json::Value; 6 | 7 | #[func] 8 | #[binding(name = "req", route = "create/{table}/{partition}/{row}")] 9 | #[binding(name = "output1", table_name = "{table}")] 10 | pub fn create_row(req: HttpRequest) -> ((), Table) { 11 | let mut table = Table::new(); 12 | { 13 | let row = table.add_row( 14 | req.route_params().get("partition").unwrap(), 15 | req.route_params().get("row").unwrap(), 16 | ); 17 | 18 | row.insert( 19 | "body".to_string(), 20 | Value::String(req.body().as_str().unwrap().to_owned()), 21 | ); 22 | } 23 | ((), table) 24 | } 25 | -------------------------------------------------------------------------------- /examples/table/src/functions/mod.rs: -------------------------------------------------------------------------------- 1 | // WARNING: This file is regenerated by the `cargo func new` command. 2 | 3 | mod create_row; 4 | mod read_row; 5 | 6 | // Export the Azure Functions here. 7 | azure_functions::export! { 8 | create_row::create_row, 9 | read_row::read_row, 10 | } 11 | -------------------------------------------------------------------------------- /examples/table/src/functions/read_row.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{ 2 | bindings::{HttpRequest, HttpResponse, Table}, 3 | func, 4 | }; 5 | 6 | #[func] 7 | #[binding(name = "_req", route = "read/{table}/{partition}/{row}")] 8 | #[binding( 9 | name = "table", 10 | table_name = "{table}", 11 | partition_key = "{partition}", 12 | row_key = "{row}" 13 | )] 14 | pub fn read_row(_req: HttpRequest, table: Table) -> HttpResponse { 15 | table.into() 16 | } 17 | -------------------------------------------------------------------------------- /examples/table/src/main.rs: -------------------------------------------------------------------------------- 1 | mod functions; 2 | 3 | fn main() { 4 | azure_functions::worker_main(std::env::args(), functions::EXPORTS); 5 | } 6 | -------------------------------------------------------------------------------- /examples/timer/.dockerignore: -------------------------------------------------------------------------------- 1 | target/ 2 | Cargo.lock 3 | .vscode/ 4 | .git/ 5 | -------------------------------------------------------------------------------- /examples/timer/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "lldb", 6 | "request": "attach", 7 | "name": "Debug", 8 | "windows": { 9 | "program": "timer-example.exe" 10 | }, 11 | "program": "timer-example", 12 | "preLaunchTask": "Launch Azure Functions Application", 13 | "postDebugTask": "Terminate Azure Functions Application" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /examples/timer/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "Launch Azure Functions Application", 6 | "type": "shell", 7 | "command": "cargo", 8 | "args": [ 9 | "func", 10 | "run" 11 | ], 12 | "presentation": { 13 | "reveal": "always", 14 | "clear": true, 15 | "focus": true 16 | }, 17 | "problemMatcher": [ 18 | { 19 | "owner": "azureFunctions", 20 | "pattern": [ 21 | { 22 | "regexp": "\\b\\B", 23 | "file": 1, 24 | "location": 2, 25 | "message": 3 26 | } 27 | ], 28 | "background": { 29 | "activeOnStart": true, 30 | "beginsPattern": "^Azure Functions Core Tools", 31 | "endsPattern": "^Application started." 32 | } 33 | } 34 | ], 35 | "isBackground": true 36 | }, 37 | { 38 | "label": "Terminate Azure Functions Application", 39 | "type": "process", 40 | "command":"${command:workbench.action.tasks.terminate}", 41 | "args": [ 42 | "Launch Azure Functions Application" 43 | ] 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /examples/timer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "timer-example" 3 | version = "0.1.0" 4 | authors = ["Peter Huene "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | azure-functions = { path = "../../azure-functions" } 9 | log = "0.4.8" 10 | 11 | [features] 12 | unstable = ["azure-functions/unstable"] 13 | -------------------------------------------------------------------------------- /examples/timer/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile-upstream:experimental 2 | 3 | FROM peterhuene/azure-functions-rs-build:0.11.0 AS build-image 4 | 5 | WORKDIR /src 6 | COPY . /src 7 | 8 | # Run with mounted cache 9 | RUN --mount=type=cache,target=/src/target \ 10 | --mount=type=cache,target=/usr/local/cargo/git \ 11 | --mount=type=cache,target=/usr/local/cargo/registry \ 12 | ["cargo", "run", "--release", "--", "init", "--script-root", "/home/site/wwwroot", "--sync-extensions"] 13 | 14 | FROM mcr.microsoft.com/azure-functions/base:2.0 as runtime-image 15 | 16 | FROM mcr.microsoft.com/dotnet/core/runtime-deps:2.2 17 | 18 | ENV AzureWebJobsScriptRoot=/home/site/wwwroot \ 19 | HOME=/home \ 20 | FUNCTIONS_WORKER_RUNTIME=Rust \ 21 | languageWorkers__workersDirectory=/home/site/wwwroot/workers 22 | 23 | # Copy the Azure Functions host from the runtime image 24 | COPY --from=runtime-image [ "/azure-functions-host", "/azure-functions-host" ] 25 | 26 | # Copy the script root contents from the build image 27 | COPY --from=build-image ["/home/site/wwwroot", "/home/site/wwwroot"] 28 | 29 | WORKDIR /home/site/wwwroot 30 | CMD [ "/azure-functions-host/Microsoft.Azure.WebJobs.Script.WebHost" ] 31 | -------------------------------------------------------------------------------- /examples/timer/README.md: -------------------------------------------------------------------------------- 1 | # Example Timer Azure Function 2 | 3 | This project is an example of a simple timer-triggered Azure Function. 4 | 5 | ## Example function implementation 6 | 7 | The example timer-triggered Azure Function that runs every minute: 8 | 9 | ```rust 10 | use azure_functions::{bindings::TimerInfo, func}; 11 | 12 | #[func] 13 | #[binding(name = "info", schedule = "0 */1 * * * *")] 14 | pub fn timer(info: TimerInfo) { 15 | log::info!("Hello from Rust!"); 16 | log::info!("Timer information: {:?}", info); 17 | } 18 | ``` 19 | 20 | # Running the example locally 21 | 22 | Because this example relies on Azure Storage to function, the `AzureWebJobsStorage` 23 | setting must be set to a connection string that the Azure Functions Host will use for 24 | the default storage connection. 25 | 26 | Add a setting for `AzureWebJobsStorage` into `local.settings.json`: 27 | 28 | ``` 29 | $ func settings add AzureWebJobsStorage 30 | ``` 31 | 32 | You may encrypt `local.settings.json`, if desired: 33 | 34 | ``` 35 | $ func settings encrypt 36 | ``` 37 | 38 | Finally, start the Azure Functions application: 39 | 40 | ``` 41 | $ cargo func run 42 | ``` 43 | 44 | # Invoking the functions 45 | 46 | ## Invoke the `timer` function 47 | 48 | The example function is automatically invoked by the Azure Functions Host when the timer expires. 49 | 50 | Wait a minute and then check the Azure Functions Host output. 51 | 52 | With any luck, you should see the following output from the Azure Functions Host: 53 | 54 | ``` 55 | nfo: Function.timer[0] 56 | Executing 'Functions.timer' (Reason='Timer fired at 2018-11-27T01:19:59.9935861+00:00', Id=2201c737-f12f-4e82-bdf2-f21969d29305) 57 | info: Function.timer.User[0] 58 | Hello from Rust! 59 | info: Function.timer.User[0] 60 | Timer information: TimerInfo { schedule_status: Some(ScheduleStatus { last: 0001-01-01T00:00:00Z, next: 2018-11-27T01:20:00Z, last_updated: 0001-01-01T00:00:00Z }), is_past_due: false } 61 | info: Function.timer[0] 62 | Executed 'Functions.timer' (Succeeded, Id=2201c737-f12f-4e82-bdf2-f21969d29305) 63 | ``` 64 | -------------------------------------------------------------------------------- /examples/timer/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "logging": { 4 | "logLevel": { 5 | "default": "Information" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/timer/local.settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "IsEncrypted": false, 3 | "Values": { 4 | "FUNCTIONS_WORKER_RUNTIME": "Rust", 5 | "languageWorkers:workersDirectory": "workers" 6 | }, 7 | "ConnectionStrings": { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/timer/src/functions/mod.rs: -------------------------------------------------------------------------------- 1 | // WARNING: This file is regenerated by the `cargo func new` command. 2 | 3 | mod timer; 4 | 5 | // Export the Azure Functions here. 6 | azure_functions::export! { 7 | timer::timer, 8 | } 9 | -------------------------------------------------------------------------------- /examples/timer/src/functions/timer.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{bindings::TimerInfo, func}; 2 | 3 | #[func] 4 | #[binding(name = "info", schedule = "0 */1 * * * *")] 5 | pub fn timer(info: TimerInfo) { 6 | log::info!("Hello from Rust!"); 7 | log::info!("Timer information: {:?}", info); 8 | } 9 | -------------------------------------------------------------------------------- /examples/timer/src/main.rs: -------------------------------------------------------------------------------- 1 | mod functions; 2 | 3 | fn main() { 4 | azure_functions::worker_main(std::env::args(), functions::EXPORTS); 5 | } 6 | -------------------------------------------------------------------------------- /examples/twilio/.dockerignore: -------------------------------------------------------------------------------- 1 | target/ 2 | Cargo.lock 3 | .vscode/ 4 | .git/ 5 | -------------------------------------------------------------------------------- /examples/twilio/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "lldb", 6 | "request": "attach", 7 | "name": "Debug", 8 | "windows": { 9 | "program": "twilio-example.exe" 10 | }, 11 | "program": "twilio-example", 12 | "preLaunchTask": "Launch Azure Functions Application", 13 | "postDebugTask": "Terminate Azure Functions Application" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /examples/twilio/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "Launch Azure Functions Application", 6 | "type": "shell", 7 | "command": "cargo", 8 | "args": [ 9 | "func", 10 | "run" 11 | ], 12 | "presentation": { 13 | "reveal": "always", 14 | "clear": true, 15 | "focus": true 16 | }, 17 | "problemMatcher": [ 18 | { 19 | "owner": "azureFunctions", 20 | "pattern": [ 21 | { 22 | "regexp": "\\b\\B", 23 | "file": 1, 24 | "location": 2, 25 | "message": 3 26 | } 27 | ], 28 | "background": { 29 | "activeOnStart": true, 30 | "beginsPattern": "^Azure Functions Core Tools", 31 | "endsPattern": "^Application started." 32 | } 33 | } 34 | ], 35 | "isBackground": true 36 | }, 37 | { 38 | "label": "Terminate Azure Functions Application", 39 | "type": "process", 40 | "command":"${command:workbench.action.tasks.terminate}", 41 | "args": [ 42 | "Launch Azure Functions Application" 43 | ] 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /examples/twilio/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "twilio-example" 3 | version = "0.1.0" 4 | authors = ["Peter Huene "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | azure-functions = { path = "../../azure-functions" } 9 | 10 | [features] 11 | unstable = ["azure-functions/unstable"] 12 | -------------------------------------------------------------------------------- /examples/twilio/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile-upstream:experimental 2 | 3 | FROM peterhuene/azure-functions-rs-build:0.11.0 AS build-image 4 | 5 | WORKDIR /src 6 | COPY . /src 7 | 8 | # Run with mounted cache 9 | RUN --mount=type=cache,target=/src/target \ 10 | --mount=type=cache,target=/usr/local/cargo/git \ 11 | --mount=type=cache,target=/usr/local/cargo/registry \ 12 | ["cargo", "run", "--release", "--", "init", "--script-root", "/home/site/wwwroot", "--sync-extensions"] 13 | 14 | FROM mcr.microsoft.com/azure-functions/base:2.0 as runtime-image 15 | 16 | FROM mcr.microsoft.com/dotnet/core/runtime-deps:2.2 17 | 18 | ENV AzureWebJobsScriptRoot=/home/site/wwwroot \ 19 | HOME=/home \ 20 | FUNCTIONS_WORKER_RUNTIME=Rust \ 21 | languageWorkers__workersDirectory=/home/site/wwwroot/workers 22 | 23 | # Copy the Azure Functions host from the runtime image 24 | COPY --from=runtime-image [ "/azure-functions-host", "/azure-functions-host" ] 25 | 26 | # Copy the script root contents from the build image 27 | COPY --from=build-image ["/home/site/wwwroot", "/home/site/wwwroot"] 28 | 29 | WORKDIR /home/site/wwwroot 30 | CMD [ "/azure-functions-host/Microsoft.Azure.WebJobs.Script.WebHost" ] 31 | -------------------------------------------------------------------------------- /examples/twilio/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "logging": { 4 | "logLevel": { 5 | "default": "Information" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/twilio/local.settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "IsEncrypted": false, 3 | "Values": { 4 | "FUNCTIONS_WORKER_RUNTIME": "Rust", 5 | "languageWorkers:workersDirectory": "workers" 6 | }, 7 | "ConnectionStrings": { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/twilio/src/functions/mod.rs: -------------------------------------------------------------------------------- 1 | // WARNING: This file is regenerated by the `cargo func new` command. 2 | 3 | mod send_sms; 4 | 5 | // Export the Azure Functions here. 6 | azure_functions::export! { 7 | send_sms::send_sms, 8 | } 9 | -------------------------------------------------------------------------------- /examples/twilio/src/functions/send_sms.rs: -------------------------------------------------------------------------------- 1 | use azure_functions::{ 2 | bindings::{HttpRequest, HttpResponse, TwilioSmsMessage}, 3 | func, 4 | }; 5 | use std::borrow::ToOwned; 6 | 7 | #[func] 8 | #[binding(name = "output1", from = "+15555555555")] 9 | pub fn send_sms(req: HttpRequest) -> (HttpResponse, TwilioSmsMessage) { 10 | let params = req.query_params(); 11 | 12 | ( 13 | "Text message sent.".into(), 14 | TwilioSmsMessage { 15 | to: params.get("to").unwrap().to_owned(), 16 | body: params.get("body").map(ToOwned::to_owned), 17 | ..Default::default() 18 | }, 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /examples/twilio/src/main.rs: -------------------------------------------------------------------------------- 1 | mod functions; 2 | 3 | fn main() { 4 | azure_functions::worker_main(std::env::args(), functions::EXPORTS); 5 | } 6 | --------------------------------------------------------------------------------