1use std::fmt::Display;
2
3use crate::analytics_engine::AnalyticsEngineDataset;
4#[cfg(feature = "d1")]
5use crate::d1::D1Database;
6use crate::email::SendEmail;
7use crate::kv::KvStore;
8use crate::rate_limit::RateLimiter;
9use crate::Ai;
10#[cfg(feature = "queue")]
11use crate::Queue;
12use crate::{durable::ObjectNamespace, Bucket, DynamicDispatcher, Fetcher, Result, SecretStore};
13use crate::{error::Error, hyperdrive::Hyperdrive};
14
15use js_sys::Object;
16use serde::de::DeserializeOwned;
17use wasm_bindgen::{prelude::*, JsCast, JsValue};
18
19#[wasm_bindgen]
20extern "C" {
21 #[derive(Debug, Clone)]
23 pub type Env;
24}
25
26unsafe impl Send for Env {}
27unsafe impl Sync for Env {}
28
29impl Env {
30 pub fn get_binding<T: EnvBinding>(&self, name: &str) -> Result<T> {
33 let binding = js_sys::Reflect::get(self, &JsValue::from(name))
34 .map_err(|_| Error::JsError(format!("Env does not contain binding `{name}`")))?;
35 if binding.is_undefined() {
36 Err(format!("Binding `{name}` is undefined.").into())
37 } else {
38 T::get(binding)
41 }
42 }
43
44 pub fn ai(&self, binding: &str) -> Result<Ai> {
45 self.get_binding::<Ai>(binding)
46 }
47
48 pub fn analytics_engine(&self, binding: &str) -> Result<AnalyticsEngineDataset> {
49 self.get_binding::<AnalyticsEngineDataset>(binding)
50 }
51
52 pub fn secret(&self, binding: &str) -> Result<Secret> {
55 self.get_binding::<Secret>(binding)
56 }
57
58 pub fn var(&self, binding: &str) -> Result<Var> {
63 self.get_binding::<Var>(binding)
64 }
65
66 pub fn object_var<T: DeserializeOwned>(&self, binding: &str) -> Result<T> {
71 Ok(serde_wasm_bindgen::from_value(
72 self.get_binding::<JsValueWrapper>(binding)?.0,
73 )?)
74 }
75
76 pub fn kv(&self, binding: &str) -> Result<KvStore> {
78 KvStore::from_this(self, binding).map_err(From::from)
79 }
80
81 pub fn durable_object(&self, binding: &str) -> Result<ObjectNamespace> {
83 self.get_binding(binding)
84 }
85
86 pub fn dynamic_dispatcher(&self, binding: &str) -> Result<DynamicDispatcher> {
88 self.get_binding(binding)
89 }
90
91 pub fn service(&self, binding: &str) -> Result<Fetcher> {
94 self.get_binding(binding)
95 }
96
97 #[cfg(feature = "queue")]
98 pub fn queue(&self, binding: &str) -> Result<Queue> {
100 self.get_binding(binding)
101 }
102
103 pub fn bucket(&self, binding: &str) -> Result<Bucket> {
105 self.get_binding(binding)
106 }
107
108 #[cfg(feature = "d1")]
110 pub fn d1(&self, binding: &str) -> Result<D1Database> {
111 self.get_binding(binding)
112 }
113
114 pub fn assets(&self, binding: &str) -> Result<Fetcher> {
116 self.get_binding(binding)
117 }
118
119 pub fn hyperdrive(&self, binding: &str) -> Result<Hyperdrive> {
120 self.get_binding(binding)
121 }
122
123 pub fn secret_store(&self, binding: &str) -> Result<SecretStore> {
125 self.get_binding(binding)
126 }
127
128 pub fn rate_limiter(&self, binding: &str) -> Result<RateLimiter> {
130 self.get_binding(binding)
131 }
132
133 pub fn send_email(&self, binding: &str) -> Result<SendEmail> {
139 self.get_binding(binding)
140 }
141}
142
143pub trait EnvBinding: Sized + JsCast {
144 const TYPE_NAME: &'static str;
145
146 fn get(val: JsValue) -> Result<Self> {
147 let obj = Object::from(val);
148 if obj.constructor().name() == Self::TYPE_NAME {
149 Ok(obj.unchecked_into())
150 } else {
151 Err(format!(
152 "Binding cannot be cast to the type {} from {}",
153 Self::TYPE_NAME,
154 obj.constructor().name()
155 )
156 .into())
157 }
158 }
159}
160
161#[repr(transparent)]
162#[derive(Debug)]
163pub struct StringBinding(JsValue);
164
165impl EnvBinding for StringBinding {
166 const TYPE_NAME: &'static str = "String";
167}
168
169impl JsCast for StringBinding {
170 fn instanceof(val: &JsValue) -> bool {
171 val.is_string()
172 }
173
174 fn unchecked_from_js(val: JsValue) -> Self {
175 StringBinding(val)
176 }
177
178 fn unchecked_from_js_ref(val: &JsValue) -> &Self {
179 unsafe { &*(val as *const JsValue as *const Self) }
181 }
182}
183
184impl AsRef<JsValue> for StringBinding {
185 fn as_ref(&self) -> &wasm_bindgen::JsValue {
186 unsafe { &*(&self.0 as *const JsValue) }
187 }
188}
189
190impl From<JsValue> for StringBinding {
191 fn from(val: JsValue) -> Self {
192 StringBinding(val)
193 }
194}
195
196impl From<StringBinding> for JsValue {
197 fn from(sec: StringBinding) -> Self {
198 sec.0
199 }
200}
201
202impl Display for StringBinding {
203 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
204 write!(f, "{}", self.0.as_string().unwrap_or_default())
205 }
206}
207
208#[repr(transparent)]
209struct JsValueWrapper(JsValue);
210
211impl EnvBinding for JsValueWrapper {
212 const TYPE_NAME: &'static str = "Object";
213}
214
215impl JsCast for JsValueWrapper {
216 fn instanceof(_: &JsValue) -> bool {
217 true
218 }
219
220 fn unchecked_from_js(val: JsValue) -> Self {
221 Self(val)
222 }
223
224 fn unchecked_from_js_ref(val: &JsValue) -> &Self {
225 unsafe { &*(val as *const JsValue as *const Self) }
227 }
228}
229
230impl From<JsValueWrapper> for wasm_bindgen::JsValue {
231 fn from(value: JsValueWrapper) -> Self {
232 value.0
233 }
234}
235
236impl AsRef<JsValue> for JsValueWrapper {
237 fn as_ref(&self) -> &JsValue {
238 &self.0
239 }
240}
241
242#[doc(inline)]
244pub use StringBinding as Secret;
245pub type Var = StringBinding;