Skip to content

Commit

Permalink
feat: add email worker support
Browse files Browse the repository at this point in the history
  • Loading branch information
LuisDuarte1 committed Aug 23, 2024
1 parent 6a5bfc1 commit 15deda7
Show file tree
Hide file tree
Showing 11 changed files with 381 additions and 0 deletions.
8 changes: 8 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions examples/email/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "email"
version = "0.1.0"
edition = "2021"

[package.metadata.release]
release = false

# https://github.com/rustwasm/wasm-pack/issues/1247
[package.metadata.wasm-pack.profile.release]
wasm-opt = false

[lib]
crate-type = ["cdylib"]

[dependencies]
worker = { workspace=true }
console_error_panic_hook = { version = "0.1.1" }
22 changes: 22 additions & 0 deletions examples/email/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use core::str;

use worker::*;

#[event(email)]
async fn email(message: ForwardableEmailMessage, _env: Env, _ctx: Context) -> Result<()> {
console_error_panic_hook::set_once();

let allow_list = vec!["another@example.com", "coworker@example.com"];
let from = message.from_envelope();

let raw: Vec<u8> = message.raw_bytes().await?;
let raw = str::from_utf8(&raw)?;
console_log!("Raw email: {}", raw);

if allow_list.contains(&from.as_str()) {
message.forward("mailbox@anotherexample.com", None).await?;
} else {
message.set_reject("Address not allowed")?;
}
Ok(())
}
6 changes: 6 additions & 0 deletions examples/email/wrangler.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
name = "email-worker"
main = "build/worker/shim.mjs"
compatibility_date = "2024-08-13"

[build]
command = "cargo install -q worker-build && worker-build --release"
5 changes: 5 additions & 0 deletions worker-build/src/js/shim.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ class Entrypoint extends WorkerEntrypoint {
async scheduled(event) {
return await imports.scheduled(event, this.env, this.ctx)
}

async email(message) {
return await imports.email(message, this.env, this.ctx)
}
}

const EXCLUDE_EXPORT = [
Expand All @@ -33,6 +37,7 @@ const EXCLUDE_EXPORT = [
"fetch",
"queue",
"scheduled",
"email",
"getMemory"
];

Expand Down
41 changes: 41 additions & 0 deletions worker-macros/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub fn expand_macro(attr: TokenStream, item: TokenStream) -> TokenStream {
Start,
#[cfg(feature = "queue")]
Queue,
Email,
}
use HandlerType::*;

Expand All @@ -28,6 +29,7 @@ pub fn expand_macro(attr: TokenStream, item: TokenStream) -> TokenStream {
"respond_with_errors" => {
respond_with_errors = true;
}
"email" => handler_type = Some(Email),
_ => panic!("Invalid attribute: {}", attr),
}
}
Expand Down Expand Up @@ -183,6 +185,45 @@ pub fn expand_macro(attr: TokenStream, item: TokenStream) -> TokenStream {

TokenStream::from(output)
}
Email => {
let input_fn_ident = Ident::new(
&(input_fn.sig.ident.to_string() + "_email_glue"),
input_fn.sig.ident.span(),
);
let wrapper_fn_ident = Ident::new("email", input_fn.sig.ident.span());
// rename the original attributed fn
input_fn.sig.ident = input_fn_ident.clone();

let wrapper_fn = quote! {
pub async fn #wrapper_fn_ident(message: ::worker::worker_sys::ForwardableEmailMessage, env: ::worker::Env, ctx: ::worker::worker_sys::Context) {
// call the original fn
let ctx = worker::Context::new(ctx);
match #input_fn_ident(::worker::ForwardableEmailMessage::from(message), env, ctx).await {
Ok(()) => {},
Err(e) => {
::worker::console_log!("{}", &e);
panic!("{}", e);
}
}
}
};

let wasm_bindgen_code =
wasm_bindgen_macro_support::expand(TokenStream::new().into(), wrapper_fn)
.expect("wasm_bindgen macro failed to expand");

let output = quote! {
#input_fn

mod _worker_email {
use ::worker::{wasm_bindgen, wasm_bindgen_futures};
use super::#input_fn_ident;
#wasm_bindgen_code
}
};

TokenStream::from(output)
}
Start => {
// save original fn name for re-use in the wrapper fn
let input_fn_ident = Ident::new(
Expand Down
2 changes: 2 additions & 0 deletions worker-sys/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mod crypto;
mod d1;
mod durable_object;
mod dynamic_dispatcher;
mod email;
mod fetcher;
mod fixed_length_stream;
mod hyperdrive;
Expand All @@ -23,6 +24,7 @@ pub use crypto::*;
pub use d1::*;
pub use durable_object::*;
pub use dynamic_dispatcher::*;
pub use email::*;
pub use fetcher::*;
pub use fixed_length_stream::*;
pub use hyperdrive::*;
Expand Down
64 changes: 64 additions & 0 deletions worker-sys/src/types/email.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use js_sys::Promise;
use wasm_bindgen::prelude::*;
use web_sys::{Headers, ReadableStream};

#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(extends=js_sys::Object)]
#[derive(Clone, PartialEq, Eq)]
pub type EmailMessage;

// TODO(lduarte): see if also accepting string is needed
#[wasm_bindgen(constructor, catch)]
pub fn new(from: &str, to: &str, raw: &str) -> Result<EmailMessage, JsValue>;

#[wasm_bindgen(method, getter)]
pub fn from(this: &EmailMessage) -> String;

#[wasm_bindgen(method, getter)]
pub fn to(this: &EmailMessage) -> String;
}

#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(extends=js_sys::Object)]
#[derive(Clone, PartialEq, Eq)]
pub type ForwardableEmailMessage;

#[wasm_bindgen(method, getter)]
pub fn from(this: &ForwardableEmailMessage) -> String;

#[wasm_bindgen(method, getter)]
pub fn to(this: &ForwardableEmailMessage) -> String;

#[wasm_bindgen(method, getter)]
pub fn raw(this: &ForwardableEmailMessage) -> ReadableStream;

// File size will never pass over 4GB so u32 is enough
#[wasm_bindgen(method, getter, js_name=rawSize)]
pub fn raw_size(this: &ForwardableEmailMessage) -> u32;

#[wasm_bindgen(method, catch, js_name=setReject)]
pub fn set_reject(this: &ForwardableEmailMessage, reason: &str) -> Result<(), JsValue>;

#[wasm_bindgen(method, catch)]
pub fn forward(
this: &ForwardableEmailMessage,
rcpt_to: &str,
headers: Headers,
) -> Result<Promise, JsValue>;

#[wasm_bindgen(method, catch)]
pub fn reply(this: &ForwardableEmailMessage, email: EmailMessage) -> Result<Promise, JsValue>;
}

#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(extends=js_sys::Object)]
#[derive(Clone, PartialEq, Eq)]
pub type SendEmail;

#[wasm_bindgen(method, catch)]
pub fn send(this: &SendEmail, email: EmailMessage) -> Result<Promise, JsValue>;

}
Loading

0 comments on commit 15deda7

Please sign in to comment.