worker/
env.rs

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