firebase_wasm/
functions.rs1use 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}