firebase_wasm/
functions.rs

1use std::{marker::PhantomData, pin::Pin};
2
3use serde::{Deserialize, Serialize};
4use std::future::Future;
5use wasm_bindgen::{prelude::*, JsCast};
6
7#[derive(Clone, Debug, Serialize, Deserialize, TypedBuilder)]
8#[builder(field_defaults(default, setter(strip_option)))]
9pub struct HttpsCallableOptions {
10    pub timeout: Option<usize>,
11}
12
13#[derive(Deserialize)]
14struct HttpsCallableResponse<T> {
15    data: T,
16}
17
18pub struct HttpsCallable<Req, Res>
19where
20    Req: Serialize,
21    Res: for<'de> Deserialize<'de>,
22{
23    callable: js_sys::Function,
24    _request_data: PhantomData<Req>,
25    _response_data: PhantomData<Res>,
26}
27
28impl<Req, Res> HttpsCallable<Req, Res>
29where
30    Req: Serialize,
31    Res: for<'de> Deserialize<'de>,
32{
33    #[track_caller]
34    fn call_(
35        &self,
36        data: Req,
37    ) -> impl Future<Output = Result<Result<Res, serde_wasm_bindgen::Error>, JsValue>> {
38        let data = serde_wasm_bindgen::to_value(&data).expect("data to serialize successfully");
39
40        let res = self.callable.call1(&JsValue::UNDEFINED, &data).unwrap();
41
42        let fut = wasm_bindgen_futures::JsFuture::from(res.unchecked_into::<js_sys::Promise>());
43
44        async move {
45            fut.await.map(|res| {
46                serde_wasm_bindgen::from_value::<HttpsCallableResponse<Res>>(res)
47                    .map(|res| res.data)
48            })
49        }
50    }
51}
52
53impl<Req, Res> FnOnce<(Req,)> for HttpsCallable<Req, Res>
54where
55    Req: Serialize + 'static,
56    Res: for<'de> Deserialize<'de> + 'static,
57{
58    type Output =
59        Pin<Box<dyn Future<Output = Result<Result<Res, serde_wasm_bindgen::Error>, JsValue>>>>;
60
61    extern "rust-call" fn call_once(self, args: (Req,)) -> Self::Output {
62        Box::pin(self.call_(args.0))
63    }
64}
65
66impl<Req, Res> FnMut<(Req,)> for HttpsCallable<Req, Res>
67where
68    Req: Serialize + 'static,
69    Res: for<'de> Deserialize<'de> + 'static,
70{
71    extern "rust-call" fn call_mut(&mut self, args: (Req,)) -> Self::Output {
72        Box::pin(self.call_(args.0))
73    }
74}
75
76impl<Req, Res> Fn<(Req,)> for HttpsCallable<Req, Res>
77where
78    Req: Serialize + 'static,
79    Res: for<'de> Deserialize<'de> + 'static,
80{
81    extern "rust-call" fn call(&self, args: (Req,)) -> Self::Output {
82        Box::pin(self.call_(args.0))
83    }
84}
85
86#[track_caller]
87pub fn https_callable<Req, Res>(
88    functions_instance: &Functions,
89    name: &str,
90    options: Option<HttpsCallableOptions>,
91) -> HttpsCallable<Req, Res>
92where
93    Req: Serialize,
94    Res: for<'de> Deserialize<'de>,
95{
96    let options = serde_wasm_bindgen::to_value(&options).unwrap();
97
98    let callable = https_callable_(functions_instance, name, options);
99
100    HttpsCallable {
101        callable,
102        _request_data: PhantomData::default(),
103        _response_data: PhantomData::default(),
104    }
105}
106
107#[wasm_bindgen(module = "firebase/functions")]
108extern "C" {
109    pub type Functions;
110
111    #[wasm_bindgen(js_name = getFunctions)]
112    pub fn get_functions() -> Functions;
113
114    #[wasm_bindgen(js_name = httpsCallable)]
115    fn https_callable_(
116        functions_instance: &Functions,
117        name: &str,
118        options: JsValue,
119    ) -> js_sys::Function;
120}