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