1use arpy::{FnRemote, MimeType, RpcClient};
5use async_trait::async_trait;
6use js_sys::Uint8Array;
7use reqwasm::http;
8
9use crate::Error;
10
11#[doc = include_doc::function_body!("tests/doc.rs", http_client, [my_app, MyAdd])]
17#[derive(Clone)]
19pub struct Connection(String);
20
21impl Connection {
22 pub fn new(url: &str) -> Self {
29 Self(url.to_string())
30 }
31}
32
33#[async_trait(?Send)]
34impl RpcClient for Connection {
35 type Error = Error;
36
37 async fn call<Args>(&self, args: Args) -> Result<Args::Output, Self::Error>
38 where
39 Args: FnRemote,
40 {
41 let content_type = MimeType::Cbor;
42 let mut body = Vec::new();
43 ciborium::ser::into_writer(&args, &mut body).unwrap();
44
45 let js_body = Uint8Array::new_with_length(body.len().try_into().unwrap());
46 js_body.copy_from(&body);
47
48 let result = http::Request::post(&format!("{}/{}", self.0, Args::ID))
49 .header(CONTENT_TYPE, content_type.as_str())
50 .header(ACCEPT, content_type.as_str())
51 .body(js_body)
52 .send()
53 .await
54 .map_err(Error::send)?;
55
56 if !result.ok() {
57 return Err(Error::Receive(format!(
58 "HTTP error code {}",
59 result.status()
60 )));
61 }
62
63 if let Some(result_type) = result.headers().get(CONTENT_TYPE) {
64 if result_type != content_type.as_str() {
65 return Err(Error::UnknownContentType(result_type));
66 }
67 }
68
69 let result_bytes = result.binary().await.map_err(Error::receive)?;
70 let result: Args::Output = ciborium::de::from_reader(result_bytes.as_slice())
71 .map_err(Error::deserialize_result)?;
72
73 Ok(result)
74 }
75}
76
77const ACCEPT: &str = "accept";
78const CONTENT_TYPE: &str = "content-type";