Add a RON API to search recipes by title + a bit of refactoring
This commit is contained in:
parent
a3f61e3711
commit
084f7ef445
26 changed files with 499 additions and 333 deletions
156
frontend/src/ron_request.rs
Normal file
156
frontend/src/ron_request.rs
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
/// This module provides a simple API for making HTTP requests to the server.
|
||||
/// For requests with a body (POST, PUT, PATCH, etc.), it uses the RON format.
|
||||
/// The RON data structures should come from the `web_api` module.
|
||||
/// For requests with parameters (GET), it uses the HTML form format.
|
||||
use common::web_api;
|
||||
use gloo::net::http::{Request, RequestBuilder};
|
||||
use serde::{Serialize, de::DeserializeOwned};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::toast::{self, Level};
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("Gloo error: {0}")]
|
||||
Gloo(#[from] gloo::net::Error),
|
||||
|
||||
#[error("RON Spanned error: {0}")]
|
||||
RonSpanned(#[from] ron::error::SpannedError),
|
||||
|
||||
#[error("RON Error: {0}")]
|
||||
Ron(#[from] ron::error::Error),
|
||||
|
||||
#[error("HTTP error: {0}")]
|
||||
Http(String),
|
||||
|
||||
#[error("Unknown error: {0}")]
|
||||
Other(String),
|
||||
}
|
||||
|
||||
type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
const CONTENT_TYPE: &str = "Content-Type"; // TODO: take it from the http crate.
|
||||
|
||||
async fn req_with_body<T, U>(url: &str, body: U, method_fn: fn(&str) -> RequestBuilder) -> Result<T>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
U: Serialize,
|
||||
{
|
||||
let request_builder = method_fn(url).header(
|
||||
CONTENT_TYPE,
|
||||
common::consts::MIME_TYPE_RON.to_str().unwrap(),
|
||||
);
|
||||
send_req(request_builder.body(web_api::to_string(body)?)?).await
|
||||
}
|
||||
|
||||
async fn req_with_params<T, U>(
|
||||
url: &str,
|
||||
params: U,
|
||||
method_fn: fn(&str) -> RequestBuilder,
|
||||
) -> Result<T>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
U: Serialize,
|
||||
{
|
||||
let mut url = url.to_string();
|
||||
url.push('?');
|
||||
serde_html_form::ser::push_to_string(&mut url, params).unwrap();
|
||||
let request_builder = method_fn(&url);
|
||||
send_req(request_builder.build()?).await
|
||||
}
|
||||
|
||||
async fn req<T>(url: &str, method_fn: fn(&str) -> RequestBuilder) -> Result<T>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
let request_builder = method_fn(url);
|
||||
send_req(request_builder.build()?).await
|
||||
}
|
||||
|
||||
async fn send_req<T>(request: Request) -> Result<T>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
match request.send().await {
|
||||
Err(error) => {
|
||||
toast::show_message_level(Level::Error, &format!("Internal server error: {}", error));
|
||||
Err(Error::Gloo(error))
|
||||
}
|
||||
Ok(response) => {
|
||||
if !response.ok() {
|
||||
toast::show_message_level(
|
||||
Level::Error,
|
||||
&format!("HTTP error: {}", response.status_text()),
|
||||
);
|
||||
Err(Error::Http(response.status_text()))
|
||||
} else {
|
||||
let mut r = response.binary().await?;
|
||||
// An empty response is considered to be an unit value.
|
||||
if r.is_empty() {
|
||||
r = b"()".to_vec();
|
||||
}
|
||||
Ok(ron::de::from_bytes::<T>(&r)?)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sends a request to the server with the given API name and body.
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// use common::web_api;
|
||||
/// let body = web_api::SetLang { lang : "en".to_string() };
|
||||
/// request::put::<(), _>("lang", body).await;
|
||||
/// ```
|
||||
pub async fn put<T, U>(url: &str, body: U) -> Result<T>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
U: Serialize,
|
||||
{
|
||||
req_with_body(url, body, Request::put).await
|
||||
}
|
||||
|
||||
pub async fn patch<T, U>(url: &str, body: U) -> Result<T>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
U: Serialize,
|
||||
{
|
||||
req_with_body(url, body, Request::patch).await
|
||||
}
|
||||
|
||||
pub async fn post<T, U>(url: &str, body: Option<U>) -> Result<T>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
U: Serialize,
|
||||
{
|
||||
match body {
|
||||
Some(body) => req_with_body(url, body, Request::post).await,
|
||||
None => req(url, Request::post).await,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn delete<T, U>(url: &str, body: Option<U>) -> Result<T>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
U: Serialize,
|
||||
{
|
||||
match body {
|
||||
Some(body) => req_with_body(url, body, Request::delete).await,
|
||||
None => req(url, Request::delete).await,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get<T>(url: &str) -> Result<T>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
req(url, Request::get).await
|
||||
}
|
||||
|
||||
pub async fn get_with_params<T, U>(url: &str, params: U) -> Result<T>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
U: Serialize,
|
||||
{
|
||||
req_with_params(url, params, Request::get).await
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue