use crate::{
routes::{RequestProcessor, RouteContext, RouteMethod},
server::Server,
};
use serde::{Serialize, de::DeserializeOwned};
use serde_json::Value;
use std::{collections::HashMap, error::Error, net::SocketAddr};
pub struct Request(pub http::Request<Value>);
pub struct Response(pub http::Response<Value>);
pub trait Route: FnMut(Request) -> Response + 'static {}
impl<T> Route for T where T: FnMut(Request) -> Response + 'static {}
pub trait TextRoute: FnMut() -> String + 'static {}
impl<T> TextRoute for T where T: FnMut() -> String + 'static {}
#[derive(Default)]
pub struct Mise {
routes: HashMap<RouteMethod, HashMap<String, RouteContext>>,
text_routes: HashMap<String, Box<dyn TextRoute>>,
}
impl Mise {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn text<F: TextRoute>(mut self, path: &str, f: F) -> Self {
self.text_routes.insert(path.to_string(), Box::new(f));
self
}
#[must_use]
pub fn get<F: Route>(mut self, path: &str, f: F) -> Self {
self.regiser_method(RouteMethod::Get, path, f);
self
}
#[must_use]
pub fn delete<F: Route>(mut self, path: &str, f: F) -> Self {
self.regiser_method(RouteMethod::Delete, path, f);
self
}
#[must_use]
pub fn post<F: Route>(mut self, path: &str, f: F) -> Self {
self.regiser_method(RouteMethod::Post, path, f);
self
}
#[must_use]
pub fn put<F: Route>(mut self, path: &str, f: F) -> Self {
self.regiser_method(RouteMethod::Put, path, f);
self
}
#[must_use]
pub fn patch<F: Route>(mut self, path: &str, f: F) -> Self {
self.regiser_method(RouteMethod::Patch, path, f);
self
}
pub fn serve(self, addr: SocketAddr) {
Server::serve(
RequestProcessor {
routes: self.routes,
text_routes: self.text_routes,
},
addr,
)
.run();
}
fn regiser_method<F: Route>(&mut self, method: RouteMethod, path: &str, f: F) {
let routes = self.routes.entry(method).or_default();
let d: Box<dyn Route> = Box::new(f);
routes.insert(path.to_string(), (path, d).into());
}
}
impl From<Request> for Value {
fn from(value: Request) -> Self {
value.0.body().to_owned()
}
}
impl From<Value> for Response {
fn from(value: Value) -> Self {
Response(http::Response::new(value))
}
}
impl From<http::StatusCode> for Response {
fn from(value: http::StatusCode) -> Self {
Response(
http::Response::builder()
.status(value)
.body(Value::Null)
.expect("Statically built body should not fail"),
)
}
}
pub trait Serializable: Serialize {
fn to_response(&self) -> Result<Response, Box<dyn Error>> {
Ok(Response(http::Response::new(serde_json::to_value(self)?)))
}
}
impl<T> Serializable for T where T: Serialize {}
pub trait Deserializable: DeserializeOwned {
fn from_request(r: Request) -> Result<Self, Box<dyn Error>> {
Ok(serde_json::from_value(r.body().to_owned())?)
}
}
impl<T> Deserializable for T where T: DeserializeOwned {}
impl Request {
pub fn path(&self) -> &str {
self.0.uri().path()
}
pub fn query(&self) -> Option<&str> {
self.0.uri().query()
}
pub fn query_param(&self, name: &str) -> Option<&str> {
let q = self.0.uri().query()?;
let f = format!("{name}=");
let idx = q.find(&f)?;
let end = q[idx..].find('&').unwrap_or(q.len());
Some(&q[idx + f.len()..end])
}
pub fn base(&self) -> &str {
let p = self.0.uri().path();
let n = self.name();
p[..p.len() - n.len()].trim_end_matches('/')
}
pub fn name(&self) -> &str {
if !self.0.uri().path().contains('/') {
return "";
}
self.0.uri().path().split('/').next_back().unwrap_or("")
}
pub fn body(&self) -> &Value {
self.0.body()
}
pub(crate) fn base_star(&self) -> Option<String> {
if self.name().is_empty() {
return None;
}
Some(format!("{}/*", self.base()))
}
}
#[cfg(test)]
mod test {
use super::*;
use serde::Deserialize;
use serde_json::json;
#[derive(Serialize, Deserialize)]
struct My {
val: usize,
}
#[test]
fn test_serde() {
let m = My { val: 1 };
let r = m.to_response().unwrap();
assert_eq!(r.0.body()["val"].clone(), 1);
let r = Request(http::Request::new(json!({"val":2})));
let m = My::from_request(r).unwrap();
assert_eq!(m.val, 2);
}
#[test]
fn test_paths() {
assert_eq!(requri("/").name(), "");
assert_eq!(requri("/a").name(), "a");
assert_eq!(requri("/a/b").name(), "b");
assert_eq!(requri("/").base(), "");
assert_eq!(requri("/a").base(), "");
assert_eq!(requri("/a/b").base(), "/a");
assert_eq!(requri("/").base_star(), None);
assert_eq!(requri("/a").base_star(), Some("/*".to_string()));
assert_eq!(requri("/a/b").base_star(), Some("/a/*".to_string()));
assert_eq!(requri("/?b=a").query_param("b"), Some("a"));
}
fn requri(uri: &str) -> Request {
Request(http::Request::builder().uri(uri).body(Value::Null).unwrap())
}
}