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}