Skip to main content

worker/
env.rs

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    /// Env contains any bindings you have associated with the Worker when you uploaded it.
22    #[derive(Debug, Clone)]
23    pub type Env;
24}
25
26unsafe impl Send for Env {}
27unsafe impl Sync for Env {}
28
29impl Env {
30    /// Access a binding that does not have a wrapper in workers-rs. Useful for internal-only or
31    /// unstable bindings.
32    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            // Can't just use JsCast::dyn_into here because the type name might not be in scope
39            // resulting in a terribly annoying javascript error which can't be caught
40            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    /// Access Secret value bindings added to your Worker via the UI or `wrangler`:
53    /// <https://developers.cloudflare.com/workers/cli-wrangler/commands#secret>
54    pub fn secret(&self, binding: &str) -> Result<Secret> {
55        self.get_binding::<Secret>(binding)
56    }
57
58    /// Get an environment variable defined in the [vars] section of your wrangler.toml or a secret
59    /// defined using `wrangler secret` as a plaintext value.
60    ///
61    /// See: <https://developers.cloudflare.com/workers/configuration/environment-variables/>
62    pub fn var(&self, binding: &str) -> Result<Var> {
63        self.get_binding::<Var>(binding)
64    }
65
66    /// Get an environment variable defined in the [vars] section of your wrangler.toml that is
67    /// defined as an object.
68    ///
69    /// See: <https://developers.cloudflare.com/workers/configuration/environment-variables/>
70    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    /// Access a Workers KV namespace by the binding name configured in your wrangler.toml file.
77    pub fn kv(&self, binding: &str) -> Result<KvStore> {
78        KvStore::from_this(self, binding).map_err(From::from)
79    }
80
81    /// Access a Durable Object namespace by the binding name configured in your wrangler.toml file.
82    pub fn durable_object(&self, binding: &str) -> Result<ObjectNamespace> {
83        self.get_binding(binding)
84    }
85
86    /// Access a Dynamic Dispatcher for dispatching events to other workers.
87    pub fn dynamic_dispatcher(&self, binding: &str) -> Result<DynamicDispatcher> {
88        self.get_binding(binding)
89    }
90
91    /// Get a [Service Binding](https://developers.cloudflare.com/workers/runtime-apis/service-bindings/)
92    /// for Worker-to-Worker communication.
93    pub fn service(&self, binding: &str) -> Result<Fetcher> {
94        self.get_binding(binding)
95    }
96
97    #[cfg(feature = "queue")]
98    /// Access a Queue by the binding name configured in your wrangler.toml file.
99    pub fn queue(&self, binding: &str) -> Result<Queue> {
100        self.get_binding(binding)
101    }
102
103    /// Access an R2 Bucket by the binding name configured in your wrangler.toml file.
104    pub fn bucket(&self, binding: &str) -> Result<Bucket> {
105        self.get_binding(binding)
106    }
107
108    /// Access a D1 Database by the binding name configured in your wrangler.toml file.
109    #[cfg(feature = "d1")]
110    pub fn d1(&self, binding: &str) -> Result<D1Database> {
111        self.get_binding(binding)
112    }
113
114    /// Access the worker assets by the binding name configured in your wrangler.toml file.
115    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    /// Access a Secret Store by the binding name configured in your wrangler.toml file.
124    pub fn secret_store(&self, binding: &str) -> Result<SecretStore> {
125        self.get_binding(binding)
126    }
127
128    /// Access a Rate Limiter by the binding name configured in your wrangler.toml file.
129    pub fn rate_limiter(&self, binding: &str) -> Result<RateLimiter> {
130        self.get_binding(binding)
131    }
132
133    /// Access a [send_email binding](https://developers.cloudflare.com/email-service/api/send-emails/workers-api/)
134    /// configured under `[[send_email]]` in your `wrangler.toml`. Use the
135    /// returned [`SendEmail`] to dispatch either a structured
136    /// [`Email`](crate::Email) or a prebuilt
137    /// [`EmailMessage`](crate::EmailMessage).
138    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        // Safety: Self is marked repr(transparent)
180        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        // Safety: Self is marked repr(transparent)
226        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/// A string value representing a binding to a secret in a Worker.
243#[doc(inline)]
244pub use StringBinding as Secret;
245/// A string value representing a binding to an environment variable in a Worker.
246pub type Var = StringBinding;