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