Files
tuono/crates/tuono_lib/src/request.rs
T

145 lines
3.5 KiB
Rust
Raw Normal View History

2025-02-14 17:39:37 +01:00
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use axum::http::{HeaderMap, Uri};
2024-06-23 20:15:33 +02:00
/// Location must match client side interface
#[derive(Serialize, Debug)]
pub struct Location {
href: String,
pathname: String,
2024-07-11 21:43:38 +02:00
#[serde(rename(serialize = "searchStr"))]
2024-06-23 20:15:33 +02:00
search_str: String,
2024-07-11 21:43:38 +02:00
search: HashMap<String, String>,
2024-06-23 20:15:33 +02:00
}
impl Location {
pub fn pathname(&self) -> &String {
&self.pathname
}
}
2024-07-09 19:00:48 +02:00
impl From<Uri> for Location {
fn from(uri: Uri) -> Self {
2024-07-11 21:43:38 +02:00
let query = uri.query().unwrap_or("");
2024-06-23 20:15:33 +02:00
Location {
2024-07-11 21:43:38 +02:00
// TODO: build correct href
2024-06-23 20:15:33 +02:00
href: uri.to_string(),
pathname: uri.path().to_string(),
2024-07-11 21:43:38 +02:00
search_str: query.to_string(),
search: serde_urlencoded::from_str(query).unwrap_or(HashMap::new()),
2024-06-23 20:15:33 +02:00
}
}
}
2024-06-02 20:32:53 +02:00
#[derive(Debug, Clone)]
2024-07-09 19:00:48 +02:00
pub struct Request {
2024-08-19 18:25:00 +02:00
pub uri: Uri,
2024-07-09 19:00:48 +02:00
pub headers: HeaderMap,
2024-06-02 20:32:53 +02:00
pub params: HashMap<String, String>,
}
2024-07-09 19:00:48 +02:00
impl Request {
pub fn new(uri: Uri, headers: HeaderMap, params: HashMap<String, String>) -> Request {
2024-06-02 20:32:53 +02:00
Request {
uri,
headers,
params,
}
}
2024-06-23 20:15:33 +02:00
pub fn location(&self) -> Location {
2024-07-09 19:00:48 +02:00
Location::from(self.uri.to_owned())
}
2025-02-14 17:39:37 +01:00
pub fn body<'de, T: Deserialize<'de>>(&'de self) -> Result<T, BodyParseError> {
if let Some(body) = self.headers.get("body") {
if let Ok(body) = body.to_str() {
let body = serde_json::from_str::<T>(body)?;
return Ok(body);
}
return Err(BodyParseError::Io(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"Failed to read body",
)));
}
Err(BodyParseError::Io(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"No body found",
)))
}
}
#[derive(Debug)]
pub enum BodyParseError {
Io(std::io::Error),
Serde(serde_json::Error),
}
impl From<serde_json::Error> for BodyParseError {
fn from(err: serde_json::Error) -> BodyParseError {
BodyParseError::Serde(err)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, Deserialize)]
struct FakeBody {
field1: bool,
field2: String,
}
#[test]
fn it_correctly_parse_the_body() {
let mut request = Request::new(
Uri::from_static("http://localhost:3000"),
HeaderMap::new(),
HashMap::new(),
);
request.headers.insert(
"body",
r#"{"field1": true, "field2": "hello"}"#.parse().unwrap(),
);
let body: FakeBody = request.body().expect("Failed to parse body");
assert!(body.field1);
assert_eq!(body.field2, "hello".to_string());
}
#[test]
fn it_should_trigger_an_error_when_no_body_is_found() {
let request = Request::new(
Uri::from_static("http://localhost:3000"),
HeaderMap::new(),
HashMap::new(),
);
let body: Result<FakeBody, BodyParseError> = request.body();
assert!(body.is_err());
}
#[test]
fn it_should_trigger_an_error_when_body_is_invalid() {
let mut request = Request::new(
Uri::from_static("http://localhost:3000"),
HeaderMap::new(),
HashMap::new(),
);
request
.headers
.insert("body", r#"{"field1": true"#.parse().unwrap());
let body: Result<FakeBody, BodyParseError> = request.body();
assert!(body.is_err());
}
}