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};
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(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
123pub trait EnvBinding: Sized + JsCast {
124 const TYPE_NAME: &'static str;
125
126 fn get(val: JsValue) -> Result<Self> {
127 let obj = Object::from(val);
128 if obj.constructor().name() == Self::TYPE_NAME {
129 Ok(obj.unchecked_into())
130 } else {
131 Err(format!(
132 "Binding cannot be cast to the type {} from {}",
133 Self::TYPE_NAME,
134 obj.constructor().name()
135 )
136 .into())
137 }
138 }
139}
140
141pub struct StringBinding(JsValue);
142
143impl EnvBinding for StringBinding {
144 const TYPE_NAME: &'static str = "String";
145}
146
147impl JsCast for StringBinding {
148 fn instanceof(val: &JsValue) -> bool {
149 val.is_string()
150 }
151
152 fn unchecked_from_js(val: JsValue) -> Self {
153 StringBinding(val)
154 }
155
156 fn unchecked_from_js_ref(val: &JsValue) -> &Self {
157 unsafe { &*(val as *const JsValue as *const Self) }
158 }
159}
160
161impl AsRef<JsValue> for StringBinding {
162 fn as_ref(&self) -> &wasm_bindgen::JsValue {
163 unsafe { &*(&self.0 as *const JsValue) }
164 }
165}
166
167impl From<JsValue> for StringBinding {
168 fn from(val: JsValue) -> Self {
169 StringBinding(val)
170 }
171}
172
173impl From<StringBinding> for JsValue {
174 fn from(sec: StringBinding) -> Self {
175 sec.0
176 }
177}
178
179impl Display for StringBinding {
180 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
181 write!(f, "{}", self.0.as_string().unwrap_or_default())
182 }
183}
184
185#[repr(transparent)]
186struct JsValueWrapper(JsValue);
187
188impl EnvBinding for JsValueWrapper {
189 const TYPE_NAME: &'static str = "Object";
190}
191
192impl JsCast for JsValueWrapper {
193 fn instanceof(_: &JsValue) -> bool {
194 true
195 }
196
197 fn unchecked_from_js(val: JsValue) -> Self {
198 Self(val)
199 }
200
201 fn unchecked_from_js_ref(val: &JsValue) -> &Self {
202 unsafe { std::mem::transmute(val) }
203 }
204}
205
206impl From<JsValueWrapper> for wasm_bindgen::JsValue {
207 fn from(value: JsValueWrapper) -> Self {
208 value.0
209 }
210}
211
212impl AsRef<JsValue> for JsValueWrapper {
213 fn as_ref(&self) -> &JsValue {
214 &self.0
215 }
216}
217
218#[doc(inline)]
220pub use StringBinding as Secret;
221pub type Var = StringBinding;