Skip to main content

telegram_webapp_sdk/api/
cloud_storage.rs

1// SPDX-FileCopyrightText: 2025 RAprogramm <andrey.rozanov.vl@gmail.com>
2// SPDX-License-Identifier: MIT
3
4use js_sys::{Array, Function, Promise, Reflect};
5use wasm_bindgen::{JsCast, prelude::*};
6use web_sys::window;
7
8/// Returns the `Telegram.WebApp.CloudStorage` object.
9fn cloud_storage_object() -> Result<JsValue, JsValue> {
10    let win = window().ok_or_else(|| JsValue::from_str("no window"))?;
11    let tg = Reflect::get(&win, &JsValue::from_str("Telegram"))?;
12    let webapp = Reflect::get(&tg, &JsValue::from_str("WebApp"))?;
13    Reflect::get(&webapp, &JsValue::from_str("CloudStorage"))
14}
15
16/// Calls `Telegram.WebApp.CloudStorage.getItem()`.
17///
18/// # Errors
19/// Returns `Err(JsValue)` if CloudStorage or the method is unavailable, or if
20/// the call fails.
21///
22/// # Examples
23/// ```no_run
24/// use telegram_webapp_sdk::api::cloud_storage::get_item;
25/// use wasm_bindgen_futures::JsFuture;
26/// # async fn run() -> Result<(), wasm_bindgen::JsValue> {
27/// let value = JsFuture::from(get_item("key")?).await?;
28/// # Ok(())
29/// # }
30/// ```
31pub fn get_item(key: &str) -> Result<Promise, JsValue> {
32    let storage = cloud_storage_object()?;
33    let func = Reflect::get(&storage, &JsValue::from_str("getItem"))?.dyn_into::<Function>()?;
34    func.call1(&storage, &JsValue::from_str(key))?
35        .dyn_into::<Promise>()
36}
37
38/// Calls `Telegram.WebApp.CloudStorage.setItem()`.
39///
40/// # Errors
41/// Returns `Err(JsValue)` if CloudStorage or the method is unavailable, or if
42/// the call fails.
43///
44/// # Examples
45/// ```no_run
46/// use telegram_webapp_sdk::api::cloud_storage::set_item;
47/// use wasm_bindgen_futures::JsFuture;
48/// # async fn run() -> Result<(), wasm_bindgen::JsValue> {
49/// JsFuture::from(set_item("key", "value")?).await?;
50/// # Ok(())
51/// # }
52/// ```
53pub fn set_item(key: &str, value: &str) -> Result<Promise, JsValue> {
54    let storage = cloud_storage_object()?;
55    let func = Reflect::get(&storage, &JsValue::from_str("setItem"))?.dyn_into::<Function>()?;
56    func.call2(&storage, &JsValue::from_str(key), &JsValue::from_str(value))?
57        .dyn_into::<Promise>()
58}
59
60/// Calls `Telegram.WebApp.CloudStorage.removeItem()`.
61///
62/// # Errors
63/// Returns `Err(JsValue)` if CloudStorage or the method is unavailable, or if
64/// the call fails.
65///
66/// # Examples
67/// ```no_run
68/// use telegram_webapp_sdk::api::cloud_storage::remove_item;
69/// use wasm_bindgen_futures::JsFuture;
70/// # async fn run() -> Result<(), wasm_bindgen::JsValue> {
71/// JsFuture::from(remove_item("key")?).await?;
72/// # Ok(())
73/// # }
74/// ```
75pub fn remove_item(key: &str) -> Result<Promise, JsValue> {
76    let storage = cloud_storage_object()?;
77    let func = Reflect::get(&storage, &JsValue::from_str("removeItem"))?.dyn_into::<Function>()?;
78    func.call1(&storage, &JsValue::from_str(key))?
79        .dyn_into::<Promise>()
80}
81
82/// Calls `Telegram.WebApp.CloudStorage.getItems()`.
83///
84/// # Errors
85/// Returns `Err(JsValue)` if CloudStorage or the method is unavailable, or if
86/// the call fails.
87///
88/// # Examples
89/// ```no_run
90/// use telegram_webapp_sdk::api::cloud_storage::get_items;
91/// use wasm_bindgen_futures::JsFuture;
92/// # async fn run() -> Result<(), wasm_bindgen::JsValue> {
93/// let _ = JsFuture::from(get_items(&["a", "b"])?).await?;
94/// # Ok(())
95/// # }
96/// ```
97pub fn get_items(keys: &[&str]) -> Result<Promise, JsValue> {
98    let storage = cloud_storage_object()?;
99    let func = Reflect::get(&storage, &JsValue::from_str("getItems"))?.dyn_into::<Function>()?;
100    let array = Array::new();
101    for key in keys {
102        array.push(&JsValue::from_str(key));
103    }
104    func.call1(&storage, &array.into())?.dyn_into::<Promise>()
105}
106
107/// Calls `Telegram.WebApp.CloudStorage.setItems()`.
108///
109/// # Errors
110/// Returns `Err(JsValue)` if CloudStorage or the method is unavailable, or if
111/// the call fails.
112///
113/// # Examples
114/// ```no_run
115/// use telegram_webapp_sdk::api::cloud_storage::set_items;
116/// use wasm_bindgen_futures::JsFuture;
117/// # async fn run() -> Result<(), wasm_bindgen::JsValue> {
118/// JsFuture::from(set_items(&[("a", "1"), ("b", "2")])?).await?;
119/// # Ok(())
120/// # }
121/// ```
122pub fn set_items(items: &[(&str, &str)]) -> Result<Promise, JsValue> {
123    let storage = cloud_storage_object()?;
124    let func = Reflect::get(&storage, &JsValue::from_str("setItems"))?.dyn_into::<Function>()?;
125    let obj = js_sys::Object::new();
126    for (key, value) in items {
127        Reflect::set(&obj, &JsValue::from_str(key), &JsValue::from_str(value))?;
128    }
129    func.call1(&storage, &obj.into())?.dyn_into::<Promise>()
130}
131
132/// Calls `Telegram.WebApp.CloudStorage.removeItems()`.
133///
134/// # Errors
135/// Returns `Err(JsValue)` if CloudStorage or the method is unavailable, or if
136/// the call fails.
137///
138/// # Examples
139/// ```no_run
140/// use telegram_webapp_sdk::api::cloud_storage::remove_items;
141/// use wasm_bindgen_futures::JsFuture;
142/// # async fn run() -> Result<(), wasm_bindgen::JsValue> {
143/// JsFuture::from(remove_items(&["a", "b"])?).await?;
144/// # Ok(())
145/// # }
146/// ```
147pub fn remove_items(keys: &[&str]) -> Result<Promise, JsValue> {
148    let storage = cloud_storage_object()?;
149    let func =
150        Reflect::get(&storage, &JsValue::from_str("removeItems"))?.dyn_into::<Function>()?;
151    let array = Array::new();
152    for key in keys {
153        array.push(&JsValue::from_str(key));
154    }
155    func.call1(&storage, &array.into())?.dyn_into::<Promise>()
156}
157
158/// Calls `Telegram.WebApp.CloudStorage.getKeys()`.
159///
160/// # Errors
161/// Returns `Err(JsValue)` if CloudStorage or the method is unavailable, or if
162/// the call fails.
163///
164/// # Examples
165/// ```no_run
166/// use telegram_webapp_sdk::api::cloud_storage::get_keys;
167/// use wasm_bindgen_futures::JsFuture;
168/// # async fn run() -> Result<(), wasm_bindgen::JsValue> {
169/// let _ = JsFuture::from(get_keys()?).await?;
170/// # Ok(())
171/// # }
172/// ```
173pub fn get_keys() -> Result<Promise, JsValue> {
174    let storage = cloud_storage_object()?;
175    let func = Reflect::get(&storage, &JsValue::from_str("getKeys"))?.dyn_into::<Function>()?;
176    func.call0(&storage)?.dyn_into::<Promise>()
177}
178
179/// Calls `Telegram.WebApp.CloudStorage.clear()`.
180///
181/// # Errors
182/// Returns `Err(JsValue)` if CloudStorage or the method is unavailable, or if
183/// the call fails.
184///
185/// # Examples
186/// ```no_run
187/// use telegram_webapp_sdk::api::cloud_storage::clear;
188/// use wasm_bindgen_futures::JsFuture;
189/// # async fn run() -> Result<(), wasm_bindgen::JsValue> {
190/// JsFuture::from(clear()?).await?;
191/// # Ok(())
192/// # }
193/// ```
194pub fn clear() -> Result<Promise, JsValue> {
195    let storage = cloud_storage_object()?;
196    let func = Reflect::get(&storage, &JsValue::from_str("clear"))?.dyn_into::<Function>()?;
197    func.call0(&storage)?.dyn_into::<Promise>()
198}
199
200#[cfg(test)]
201mod tests {
202    #![allow(dead_code)]
203    use js_sys::{Array, Function, Object, Reflect};
204    use wasm_bindgen_futures::JsFuture;
205    use wasm_bindgen_test::{wasm_bindgen_test, wasm_bindgen_test_configure};
206    use web_sys::window;
207
208    use super::*;
209
210    wasm_bindgen_test_configure!(run_in_browser);
211
212    fn setup_cloud_storage() -> Object {
213        let win = window().unwrap();
214        let telegram = Object::new();
215        let webapp = Object::new();
216        let storage = Object::new();
217        let _ = Reflect::set(&win, &"Telegram".into(), &telegram);
218        let _ = Reflect::set(&telegram, &"WebApp".into(), &webapp);
219        let _ = Reflect::set(&webapp, &"CloudStorage".into(), &storage);
220        storage
221    }
222
223    #[wasm_bindgen_test(async)]
224    async fn get_item_ok() {
225        let storage = setup_cloud_storage();
226        let func =
227            Function::new_with_args("key", "this.called = key; return Promise.resolve('val');");
228        let _ = Reflect::set(&storage, &"getItem".into(), &func);
229        let value = JsFuture::from(get_item("test").unwrap()).await.unwrap();
230        assert_eq!(value.as_string(), Some("val".to_string()));
231        assert_eq!(
232            Reflect::get(&storage, &"called".into())
233                .unwrap()
234                .as_string(),
235            Some("test".into())
236        );
237    }
238
239    #[wasm_bindgen_test]
240    fn get_item_err() {
241        let _ = setup_cloud_storage();
242        assert!(get_item("test").is_err());
243    }
244
245    #[wasm_bindgen_test(async)]
246    async fn set_item_ok() {
247        let storage = setup_cloud_storage();
248        let func = Function::new_with_args(
249            "key, value",
250            "this.called = key + ':' + value; return Promise.resolve();"
251        );
252        let _ = Reflect::set(&storage, &"setItem".into(), &func);
253        JsFuture::from(set_item("a", "b").unwrap()).await.unwrap();
254        assert_eq!(
255            Reflect::get(&storage, &"called".into())
256                .unwrap()
257                .as_string(),
258            Some("a:b".into())
259        );
260    }
261
262    #[wasm_bindgen_test]
263    fn set_item_err() {
264        let _ = setup_cloud_storage();
265        assert!(set_item("a", "b").is_err());
266    }
267
268    #[wasm_bindgen_test(async)]
269    async fn remove_item_ok() {
270        let storage = setup_cloud_storage();
271        let func = Function::new_with_args("key", "this.called = key; return Promise.resolve();");
272        let _ = Reflect::set(&storage, &"removeItem".into(), &func);
273        JsFuture::from(remove_item("k").unwrap()).await.unwrap();
274        assert_eq!(
275            Reflect::get(&storage, &"called".into())
276                .unwrap()
277                .as_string(),
278            Some("k".into())
279        );
280    }
281
282    #[wasm_bindgen_test]
283    fn remove_item_err() {
284        let _ = setup_cloud_storage();
285        assert!(remove_item("k").is_err());
286    }
287
288    #[wasm_bindgen_test(async)]
289    async fn get_items_ok() {
290        let storage = setup_cloud_storage();
291        let func = Function::new_with_args(
292            "keys",
293            "this.called = keys; return Promise.resolve({a: '1', b: '2'});"
294        );
295        let _ = Reflect::set(&storage, &"getItems".into(), &func);
296        let result = JsFuture::from(get_items(&["a", "b"]).unwrap())
297            .await
298            .unwrap();
299        let obj = result.dyn_into::<Object>().unwrap();
300        assert_eq!(
301            Reflect::get(&obj, &"a".into()).unwrap().as_string(),
302            Some("1".into())
303        );
304        assert_eq!(
305            Reflect::get(&obj, &"b".into()).unwrap().as_string(),
306            Some("2".into())
307        );
308        let called = Reflect::get(&storage, &"called".into()).unwrap();
309        let arr = Array::from(&called);
310        assert_eq!(arr.get(0).as_string(), Some("a".into()));
311        assert_eq!(arr.get(1).as_string(), Some("b".into()));
312    }
313
314    #[wasm_bindgen_test]
315    fn get_items_err() {
316        let _ = setup_cloud_storage();
317        assert!(get_items(&["a"]).is_err());
318    }
319
320    #[wasm_bindgen_test(async)]
321    async fn set_items_ok() {
322        let storage = setup_cloud_storage();
323        let func =
324            Function::new_with_args("items", "this.called = items; return Promise.resolve();");
325        let _ = Reflect::set(&storage, &"setItems".into(), &func);
326        JsFuture::from(set_items(&[("a", "1"), ("b", "2")]).unwrap())
327            .await
328            .unwrap();
329        let called = Reflect::get(&storage, &"called".into()).unwrap();
330        assert_eq!(
331            Reflect::get(&called, &"a".into()).unwrap().as_string(),
332            Some("1".into())
333        );
334        assert_eq!(
335            Reflect::get(&called, &"b".into()).unwrap().as_string(),
336            Some("2".into())
337        );
338    }
339
340    #[wasm_bindgen_test]
341    fn set_items_err() {
342        let _ = setup_cloud_storage();
343        assert!(set_items(&[("a", "1")]).is_err());
344    }
345
346    #[wasm_bindgen_test(async)]
347    async fn remove_items_ok() {
348        let storage = setup_cloud_storage();
349        let func =
350            Function::new_with_args("keys", "this.called = keys; return Promise.resolve();");
351        let _ = Reflect::set(&storage, &"removeItems".into(), &func);
352        JsFuture::from(remove_items(&["a", "b"]).unwrap())
353            .await
354            .unwrap();
355        let called = Reflect::get(&storage, &"called".into()).unwrap();
356        let arr = Array::from(&called);
357        assert_eq!(arr.get(0).as_string(), Some("a".into()));
358        assert_eq!(arr.get(1).as_string(), Some("b".into()));
359    }
360
361    #[wasm_bindgen_test]
362    fn remove_items_err() {
363        let _ = setup_cloud_storage();
364        assert!(remove_items(&["a"]).is_err());
365    }
366
367    #[wasm_bindgen_test(async)]
368    async fn get_keys_ok() {
369        let storage = setup_cloud_storage();
370        let func = Function::new_no_args("return Promise.resolve(['x', 'y']);");
371        let _ = Reflect::set(&storage, &"getKeys".into(), &func);
372        let result = JsFuture::from(get_keys().unwrap()).await.unwrap();
373        let arr = Array::from(&result);
374        assert_eq!(arr.get(0).as_string(), Some("x".into()));
375        assert_eq!(arr.get(1).as_string(), Some("y".into()));
376    }
377
378    #[wasm_bindgen_test]
379    fn get_keys_err() {
380        let _ = setup_cloud_storage();
381        assert!(get_keys().is_err());
382    }
383
384    #[wasm_bindgen_test(async)]
385    async fn clear_ok() {
386        let storage = setup_cloud_storage();
387        let func = Function::new_no_args("this.called = true; return Promise.resolve();");
388        let _ = Reflect::set(&storage, &"clear".into(), &func);
389        JsFuture::from(clear().unwrap()).await.unwrap();
390        assert!(
391            Reflect::get(&storage, &"called".into())
392                .unwrap()
393                .as_bool()
394                .unwrap()
395        );
396    }
397
398    #[wasm_bindgen_test]
399    fn clear_err() {
400        let _ = setup_cloud_storage();
401        assert!(clear().is_err());
402    }
403}