Skip to main content

worker/
email.rs

1use crate::{env::EnvBinding, send::SendFuture, Error, Result};
2use serde::de::DeserializeOwned;
3use wasm_bindgen::{JsCast, JsValue};
4use wasm_bindgen_futures::JsFuture;
5use worker_sys::{EmailMessage as EmailMessageSys, SendEmail as SendEmailSys};
6
7#[derive(Debug, Clone)]
8pub struct EmailMessage(EmailMessageSys);
9
10#[derive(Debug, Clone)]
11pub struct SendEmail(SendEmailSys);
12
13unsafe impl Send for EmailMessage {}
14unsafe impl Sync for EmailMessage {}
15unsafe impl Send for SendEmail {}
16unsafe impl Sync for SendEmail {}
17
18impl EmailMessage {
19    pub fn from_address(&self) -> Option<String> {
20        self.0.from()
21    }
22
23    pub fn to_address(&self) -> Option<String> {
24        self.0.to()
25    }
26
27    pub fn raw(&self) -> JsValue {
28        self.0.raw()
29    }
30
31    pub fn headers(&self) -> JsValue {
32        self.0.headers()
33    }
34
35    pub fn raw_size(&self) -> Option<u64> {
36        self.0.raw_size().map(|v| v.round() as u64)
37    }
38
39    pub fn set_reject(&self, reason: &str) -> Result<()> {
40        self.0.set_reject(reason).map_err(Error::from)
41    }
42
43    pub async fn forward<U: DeserializeOwned>(
44        &self,
45        rcpt_to: &str,
46        headers: Option<JsValue>,
47    ) -> Result<U> {
48        let promise = self
49            .0
50            .forward(rcpt_to, headers.unwrap_or(JsValue::UNDEFINED))?;
51        let output = SendFuture::new(JsFuture::from(promise)).await;
52        let value = output.map_err(Error::from)?;
53        Ok(serde_wasm_bindgen::from_value(value)?)
54    }
55
56    pub async fn reply<U: DeserializeOwned>(&self, message: &EmailMessage) -> Result<U> {
57        let promise = self.0.reply(&message.0)?;
58        let output = SendFuture::new(JsFuture::from(promise)).await;
59        let value = output.map_err(Error::from)?;
60        Ok(serde_wasm_bindgen::from_value(value)?)
61    }
62}
63
64impl SendEmail {
65    pub async fn send<U: DeserializeOwned>(&self, message: &EmailMessage) -> Result<U> {
66        let promise = self.0.send(&message.0)?;
67        let output = SendFuture::new(JsFuture::from(promise)).await;
68        let value = output.map_err(Error::from)?;
69        Ok(serde_wasm_bindgen::from_value(value)?)
70    }
71}
72
73impl From<EmailMessageSys> for EmailMessage {
74    fn from(value: EmailMessageSys) -> Self {
75        Self(value)
76    }
77}
78
79impl AsRef<JsValue> for EmailMessage {
80    fn as_ref(&self) -> &JsValue {
81        &self.0
82    }
83}
84
85impl EnvBinding for EmailMessage {
86    const TYPE_NAME: &'static str = "Object";
87
88    fn get(val: JsValue) -> Result<Self> {
89        if !val.is_object() {
90            return Err("Binding cannot be cast to EmailMessage from non-object value".into());
91        }
92
93        let has_from = js_sys::Reflect::has(&val, &JsValue::from("from"))?;
94        let has_to = js_sys::Reflect::has(&val, &JsValue::from("to"))?;
95        if !has_from || !has_to {
96            return Err("Binding cannot be cast to EmailMessage: missing `from` or `to` field".into());
97        }
98
99        Ok(Self(val.unchecked_into()))
100    }
101}
102
103impl JsCast for EmailMessage {
104    fn instanceof(val: &JsValue) -> bool {
105        val.is_object()
106    }
107
108    fn unchecked_from_js(val: JsValue) -> Self {
109        Self(val.unchecked_into())
110    }
111
112    fn unchecked_from_js_ref(val: &JsValue) -> &Self {
113        unsafe { &*(val as *const JsValue as *const Self) }
114    }
115}
116
117impl From<EmailMessage> for JsValue {
118    fn from(value: EmailMessage) -> Self {
119        value.0.into()
120    }
121}
122
123impl JsCast for SendEmail {
124    fn instanceof(val: &JsValue) -> bool {
125        val.is_object()
126    }
127
128    fn unchecked_from_js(val: JsValue) -> Self {
129        Self(val.unchecked_into())
130    }
131
132    fn unchecked_from_js_ref(val: &JsValue) -> &Self {
133        unsafe { &*(val as *const JsValue as *const Self) }
134    }
135}
136
137impl AsRef<JsValue> for SendEmail {
138    fn as_ref(&self) -> &JsValue {
139        &self.0
140    }
141}
142
143impl EnvBinding for SendEmail {
144    const TYPE_NAME: &'static str = "Object";
145
146    fn get(val: JsValue) -> Result<Self> {
147        if !val.is_object() {
148            return Err("Binding cannot be cast to SendEmail from non-object value".into());
149        }
150
151        let has_send = js_sys::Reflect::has(&val, &JsValue::from("send"))?;
152        if !has_send {
153            return Err("Binding cannot be cast to SendEmail: missing `send` method".into());
154        }
155
156        Ok(Self(val.unchecked_into()))
157    }
158}
159
160impl From<JsValue> for SendEmail {
161    fn from(val: JsValue) -> Self {
162        Self(val.unchecked_into())
163    }
164}
165
166impl From<SendEmail> for JsValue {
167    fn from(value: SendEmail) -> Self {
168        value.0.into()
169    }
170}