http.rs
⎇
Raw
1use std::fmt;
2
3use gloo_net::http::{Request, 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
23pub async fn post<T: DeserializeOwned>(path: &str, body: &impl Serialize) -> Result<T, Error> {
24 let req = Request::post(path).json(body).map_err(|e| Error {
25 status: 0,
26 msg: e.to_string(),
27 })?;
28 read(path, req.send().await).await
29}
30
31pub async fn put<T: DeserializeOwned>(path: &str, body: &impl Serialize) -> Result<T, Error> {
32 let req = Request::put(path).json(body).map_err(|e| Error {
33 status: 0,
34 msg: e.to_string(),
35 })?;
36 read(path, req.send().await).await
37}
38
39pub async fn delete(path: &str) -> Result<(), Error> {
40 read(path, Request::delete(path).send().await).await
41}
42
43async fn read<T: DeserializeOwned>(
44 path: &str,
45 res: Result<Response, gloo_net::Error>,
46) -> Result<T, Error> {
47 let res = res.map_err(|e| Error {
48 status: 0,
49 msg: format!("network error: {e}"),
50 })?;
51 // The session has ended. A reload shows the login form.
52 if res.status() == 401
53 && !matches!(path, "/api/login" | "/api/me")
54 && !path.starts_with("/api/guest")
55 {
56 let _ = web_sys::window().unwrap().location().reload();
57 }
58 if !res.ok() {
59 let body = res.text().await.unwrap_or_default();
60 let msg = if body.is_empty() {
61 res.status_text()
62 } else {
63 body
64 };
65 return Err(Error {
66 status: res.status(),
67 msg,
68 });
69 }
70 res.json().await.map_err(|e| Error {
71 status: 0,
72 msg: e.to_string(),
73 })
74}
75