http.rs
⎇
Raw
1use std::fmt;
2
3use gloo_net::http::{Request, RequestBuilder, Response};
4use serde::Serialize;
5
6use crate::i18n::tr;
7use serde::de::DeserializeOwned;
8
9#[derive(Clone)]
10pub struct Error {
11 pub status: u16,
12 pub msg: String,
13}
14
15impl fmt::Display for Error {
16 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
17 f.write_str(&self.msg)
18 }
19}
20
21pub async fn get<T: DeserializeOwned>(path: &str) -> Result<T, Error> {
22 read(path, Request::get(path).send().await).await
23}
24
25/// For the app's device mode. A 401 there means the device was removed, not that a session ended.
26pub async fn get_bearer<T: DeserializeOwned>(path: &str, token: &str) -> Result<T, Error> {
27 let res = Request::get(path)
28 .header("Authorization", &format!("Bearer {token}"))
29 .send()
30 .await;
31 read("", res).await
32}
33
34pub async fn post<T: DeserializeOwned>(path: &str, body: &impl Serialize) -> Result<T, Error> {
35 send(path, Request::post(path), body).await
36}
37
38pub async fn put<T: DeserializeOwned>(path: &str, body: &impl Serialize) -> Result<T, Error> {
39 send(path, Request::put(path), body).await
40}
41
42async fn send<T: DeserializeOwned>(
43 path: &str,
44 req: RequestBuilder,
45 body: &impl Serialize,
46) -> Result<T, Error> {
47 let req = req.json(body).map_err(|e| Error {
48 status: 0,
49 msg: e.to_string(),
50 })?;
51 read(path, req.send().await).await
52}
53
54pub async fn delete(path: &str) -> Result<(), Error> {
55 read(path, Request::delete(path).send().await).await
56}
57
58async fn read<T: DeserializeOwned>(
59 path: &str,
60 res: Result<Response, gloo_net::Error>,
61) -> Result<T, Error> {
62 let res = res.map_err(|e| Error {
63 status: 0,
64 msg: tr(&format!("network error: {e}")),
65 })?;
66 // The session has ended. A reload shows the login form.
67 if res.status() == 401
68 && !matches!(path, "" | "/api/login" | "/api/me")
69 && !path.starts_with("/api/guest")
70 {
71 let _ = web_sys::window().unwrap().location().reload();
72 }
73 if !res.ok() {
74 let body = res.text().await.unwrap_or_default();
75 // The server writes its messages in English. They are translated here, in one place.
76 let msg = if body.is_empty() {
77 res.status_text()
78 } else {
79 tr(&body)
80 };
81 return Err(Error {
82 status: res.status(),
83 msg,
84 });
85 }
86 res.json().await.map_err(|e| Error {
87 status: 0,
88 msg: e.to_string(),
89 })
90}
91