1use std::fmt::Display;
2
3use crate::analytics_engine::AnalyticsEngineDataset;
4#[cfg(feature = "d1")]
5use crate::d1::D1Database;
6#[cfg(feature = "queue")]
7use crate::Queue;
8use crate::{durable::ObjectNamespace, Bucket, DynamicDispatcher, Fetcher, Result, SecretStore};
9use crate::{error::Error, hyperdrive::Hyperdrive};
10
11use crate::Ai;
12
13use js_sys::Object;
14use serde::de::DeserializeOwned;
15use wasm_bindgen::{prelude::*, JsCast, JsValue};
16use worker_kv::KvStore;
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
128pub trait EnvBinding: Sized + JsCast {
129 const TYPE_NAME: &'static str;
130
131 fn get(val: JsValue) -> Result<Self> {
132 let obj = Object::from(val);
133 if obj.constructor().name() == Self::TYPE_NAME {
134 Ok(obj.unchecked_into())
135 } else {
136 Err(format!(
137 "Binding cannot be cast to the type {} from {}",
138 Self::TYPE_NAME,
139 obj.constructor().name()
140 )
141 .into())
142 }
143 }
144}
145
146#[repr(transparent)]
147#[derive(Debug)]
148pub struct StringBinding(JsValue);
149
150impl EnvBinding for StringBinding {
151 const TYPE_NAME: &'static str = "String";
152}
153
154impl JsCast for StringBinding {
155 fn instanceof(val: &JsValue) -> bool {
156 val.is_string()
157 }
158
159 fn unchecked_from_js(val: JsValue) -> Self {
160 StringBinding(val)
161 }
162
163 fn unchecked_from_js_ref(val: &JsValue) -> &Self {
164 unsafe { &*(val as *const JsValue as *const Self) }
166 }
167}
168
169impl AsRef<JsValue> for StringBinding {
170 fn as_ref(&self) -> &wasm_bindgen::JsValue {
171 unsafe { &*(&self.0 as *const JsValue) }
172 }
173}
174
175impl From<JsValue> for StringBinding {
176 fn from(val: JsValue) -> Self {
177 StringBinding(val)
178 }
179}
180
181impl From<StringBinding> for JsValue {
182 fn from(sec: StringBinding) -> Self {
183 sec.0
184 }
185}
186
187impl Display for StringBinding {
188 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
189 write!(f, "{}", self.0.as_string().unwrap_or_default())
190 }
191}
192
193#[repr(transparent)]
194struct JsValueWrapper(JsValue);
195
196impl EnvBinding for JsValueWrapper {
197 const TYPE_NAME: &'static str = "Object";
198}
199
200impl JsCast for JsValueWrapper {
201 fn instanceof(_: &JsValue) -> bool {
202 true
203 }
204
205 fn unchecked_from_js(val: JsValue) -> Self {
206 Self(val)
207 }
208
209 fn unchecked_from_js_ref(val: &JsValue) -> &Self {
210 unsafe { &*(val as *const JsValue as *const Self) }
212 }
213}
214
215impl From<JsValueWrapper> for wasm_bindgen::JsValue {
216 fn from(value: JsValueWrapper) -> Self {
217 value.0
218 }
219}
220
221impl AsRef<JsValue> for JsValueWrapper {
222 fn as_ref(&self) -> &JsValue {
223 &self.0
224 }
225}
226
227#[doc(inline)]
229pub use StringBinding as Secret;
230pub type Var = StringBinding;