Skip to main content

worker/
env.rs

1use std::fmt::Display;
2
3use crate::analytics_engine::AnalyticsEngineDataset;
4use crate::browser_rendering::BrowserRendering;
5#[cfg(feature = "d1")]
6use crate::d1::D1Database;
7use crate::kv::KvStore;
8use crate::mtls_certificate::MtlsCertificate;
9use crate::rate_limit::RateLimiter;
10use crate::Ai;
11use crate::DynamicWorkerLoader;
12use crate::SendEmail;
13use crate::ImagesBinding;
14use crate::VectorizeIndex;
15use crate::WorkflowBinding;
16use crate::WorkerVersionMetadata;
17#[cfg(feature = "queue")]
18use crate::Queue;
19use crate::{durable::ObjectNamespace, Bucket, DynamicDispatcher, Fetcher, Result, SecretStore};
20use crate::{error::Error, hyperdrive::Hyperdrive};
21
22use js_sys::Object;
23use serde::de::DeserializeOwned;
24use wasm_bindgen::{prelude::*, JsCast, JsValue};
25
26#[wasm_bindgen]
27extern "C" {
28    /// Env contains any bindings you have associated with the Worker when you uploaded it.
29    #[derive(Debug, Clone)]
30    pub type Env;
31}
32
33unsafe impl Send for Env {}
34unsafe impl Sync for Env {}
35
36impl Env {
37    /// Access a binding that does not have a wrapper in workers-rs. Useful for internal-only or
38    /// unstable bindings.
39    pub fn get_binding<T: EnvBinding>(&self, name: &str) -> Result<T> {
40        let binding = js_sys::Reflect::get(self, &JsValue::from(name))
41            .map_err(|_| Error::JsError(format!("Env does not contain binding `{name}`")))?;
42        if binding.is_undefined() {
43            Err(format!("Binding `{name}` is undefined.").into())
44        } else {
45            // Can't just use JsCast::dyn_into here because the type name might not be in scope
46            // resulting in a terribly annoying javascript error which can't be caught
47            T::get(binding)
48        }
49    }
50
51    pub fn ai(&self, binding: &str) -> Result<Ai> {
52        self.get_binding::<Ai>(binding)
53    }
54
55    pub fn analytics_engine(&self, binding: &str) -> Result<AnalyticsEngineDataset> {
56        self.get_binding::<AnalyticsEngineDataset>(binding)
57    }
58
59    /// Access a Browser Rendering binding by the name configured in your wrangler.toml file.
60    pub fn browser_rendering(&self, binding: &str) -> Result<BrowserRendering> {
61        self.get_binding::<BrowserRendering>(binding)
62    }
63
64    /// Access Secret value bindings added to your Worker via the UI or `wrangler`:
65    /// <https://developers.cloudflare.com/workers/cli-wrangler/commands#secret>
66    pub fn secret(&self, binding: &str) -> Result<Secret> {
67        self.get_binding::<Secret>(binding)
68    }
69
70    /// Get an environment variable defined in the [vars] section of your wrangler.toml or a secret
71    /// defined using `wrangler secret` as a plaintext value.
72    ///
73    /// See: <https://developers.cloudflare.com/workers/configuration/environment-variables/>
74    pub fn var(&self, binding: &str) -> Result<Var> {
75        self.get_binding::<Var>(binding)
76    }
77
78    /// Get an environment variable defined in the [vars] section of your wrangler.toml that is
79    /// defined as an object.
80    ///
81    /// See: <https://developers.cloudflare.com/workers/configuration/environment-variables/>
82    pub fn object_var<T: DeserializeOwned>(&self, binding: &str) -> Result<T> {
83        Ok(serde_wasm_bindgen::from_value(
84            self.get_binding::<JsValueWrapper>(binding)?.0,
85        )?)
86    }
87
88    /// Access a Workers KV namespace by the binding name configured in your wrangler.toml file.
89    pub fn kv(&self, binding: &str) -> Result<KvStore> {
90        KvStore::from_this(self, binding).map_err(From::from)
91    }
92
93    /// Access a Durable Object namespace by the binding name configured in your wrangler.toml file.
94    pub fn durable_object(&self, binding: &str) -> Result<ObjectNamespace> {
95        self.get_binding(binding)
96    }
97
98    /// Access a Dynamic Dispatcher for dispatching events to other workers.
99    pub fn dynamic_dispatcher(&self, binding: &str) -> Result<DynamicDispatcher> {
100        self.get_binding(binding)
101    }
102
103    /// Access a Dynamic Worker Loader by the binding name configured in your wrangler.toml file.
104    pub fn dynamic_worker_loader(&self, binding: &str) -> Result<DynamicWorkerLoader> {
105        self.get_binding(binding)
106    }
107
108    /// Get a [Service Binding](https://developers.cloudflare.com/workers/runtime-apis/service-bindings/)
109    /// for Worker-to-Worker communication.
110    pub fn service(&self, binding: &str) -> Result<Fetcher> {
111        self.get_binding(binding)
112    }
113
114    #[cfg(feature = "queue")]
115    /// Access a Queue by the binding name configured in your wrangler.toml file.
116    pub fn queue(&self, binding: &str) -> Result<Queue> {
117        self.get_binding(binding)
118    }
119
120    /// Access an R2 Bucket by the binding name configured in your wrangler.toml file.
121    pub fn bucket(&self, binding: &str) -> Result<Bucket> {
122        self.get_binding(binding)
123    }
124
125    /// Access a D1 Database by the binding name configured in your wrangler.toml file.
126    #[cfg(feature = "d1")]
127    pub fn d1(&self, binding: &str) -> Result<D1Database> {
128        self.get_binding(binding)
129    }
130
131    /// Access the worker assets by the binding name configured in your wrangler.toml file.
132    pub fn assets(&self, binding: &str) -> Result<Fetcher> {
133        self.get_binding(binding)
134    }
135
136    /// Access an Images binding by the name configured in your wrangler.toml file.
137    pub fn images(&self, binding: &str) -> Result<ImagesBinding> {
138        self.get_binding(binding)
139    }
140
141    pub fn hyperdrive(&self, binding: &str) -> Result<Hyperdrive> {
142        self.get_binding(binding)
143    }
144
145    /// Access an mTLS certificate binding by the name configured in your wrangler.toml file.
146    pub fn mtls_certificate(&self, binding: &str) -> Result<MtlsCertificate> {
147        self.get_binding(binding)
148    }
149
150    /// Access a Secret Store by the binding name configured in your wrangler.toml file.
151    pub fn secret_store(&self, binding: &str) -> Result<SecretStore> {
152        self.get_binding(binding)
153    }
154
155    /// Access a Rate Limiter by the binding name configured in your wrangler.toml file.
156    pub fn rate_limiter(&self, binding: &str) -> Result<RateLimiter> {
157        self.get_binding(binding)
158    }
159
160    /// Access a Vectorize index binding by the name configured in your wrangler.toml file.
161    pub fn vectorize(&self, binding: &str) -> Result<VectorizeIndex> {
162        self.get_binding(binding)
163    }
164
165    /// Access a Workflows binding by the name configured in your wrangler.toml file.
166    pub fn workflows(&self, binding: &str) -> Result<WorkflowBinding> {
167        self.get_binding(binding)
168    }
169
170    /// Access an email sender binding by the name configured in your wrangler.toml file.
171    pub fn send_email(&self, binding: &str) -> Result<SendEmail> {
172        self.get_binding(binding)
173    }
174
175    /// Access Worker version metadata binding by configured name.
176    pub fn version_metadata(&self, binding: &str) -> Result<WorkerVersionMetadata> {
177        self.get_binding(binding)
178    }
179}
180
181pub trait EnvBinding: Sized + JsCast {
182    const TYPE_NAME: &'static str;
183
184    fn get(val: JsValue) -> Result<Self> {
185        let obj = Object::from(val);
186        if obj.constructor().name() == Self::TYPE_NAME {
187            Ok(obj.unchecked_into())
188        } else {
189            Err(format!(
190                "Binding cannot be cast to the type {} from {}",
191                Self::TYPE_NAME,
192                obj.constructor().name()
193            )
194            .into())
195        }
196    }
197}
198
199#[repr(transparent)]
200#[derive(Debug)]
201pub struct StringBinding(JsValue);
202
203impl EnvBinding for StringBinding {
204    const TYPE_NAME: &'static str = "String";
205}
206
207impl JsCast for StringBinding {
208    fn instanceof(val: &JsValue) -> bool {
209        val.is_string()
210    }
211
212    fn unchecked_from_js(val: JsValue) -> Self {
213        StringBinding(val)
214    }
215
216    fn unchecked_from_js_ref(val: &JsValue) -> &Self {
217        // Safety: Self is marked repr(transparent)
218        unsafe { &*(val as *const JsValue as *const Self) }
219    }
220}
221
222impl AsRef<JsValue> for StringBinding {
223    fn as_ref(&self) -> &wasm_bindgen::JsValue {
224        unsafe { &*(&self.0 as *const JsValue) }
225    }
226}
227
228impl From<JsValue> for StringBinding {
229    fn from(val: JsValue) -> Self {
230        StringBinding(val)
231    }
232}
233
234impl From<StringBinding> for JsValue {
235    fn from(sec: StringBinding) -> Self {
236        sec.0
237    }
238}
239
240impl Display for StringBinding {
241    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
242        write!(f, "{}", self.0.as_string().unwrap_or_default())
243    }
244}
245
246#[repr(transparent)]
247struct JsValueWrapper(JsValue);
248
249impl EnvBinding for JsValueWrapper {
250    const TYPE_NAME: &'static str = "Object";
251}
252
253impl JsCast for JsValueWrapper {
254    fn instanceof(_: &JsValue) -> bool {
255        true
256    }
257
258    fn unchecked_from_js(val: JsValue) -> Self {
259        Self(val)
260    }
261
262    fn unchecked_from_js_ref(val: &JsValue) -> &Self {
263        // Safety: Self is marked repr(transparent)
264        unsafe { &*(val as *const JsValue as *const Self) }
265    }
266}
267
268impl From<JsValueWrapper> for wasm_bindgen::JsValue {
269    fn from(value: JsValueWrapper) -> Self {
270        value.0
271    }
272}
273
274impl AsRef<JsValue> for JsValueWrapper {
275    fn as_ref(&self) -> &JsValue {
276        &self.0
277    }
278}
279
280/// A string value representing a binding to a secret in a Worker.
281#[doc(inline)]
282pub use StringBinding as Secret;
283/// A string value representing a binding to an environment variable in a Worker.
284pub type Var = StringBinding;