mise_server/
mise.rs

1use crate::server::Server;
2use http::StatusCode;
3use serde_json::Value;
4use std::{
5    collections::HashMap,
6    net::SocketAddr,
7    panic::{AssertUnwindSafe, catch_unwind},
8};
9use tracing::trace;
10
11pub struct Request(pub http::Request<Value>);
12pub struct Response(pub http::Response<Value>);
13
14/// Final routes are always in JSON.
15pub trait Route: Fn(Request) -> Response + 'static {}
16impl<T> Route for T where T: Fn(Request) -> Response + 'static {}
17
18/// Server resource and entry point.
19#[derive(Default)]
20pub struct Mise {
21    routes: HashMap<String, Box<dyn Route>>,
22}
23
24impl Mise {
25    /// Create a new default server.
26    pub fn new() -> Self {
27        Self::default()
28    }
29
30    /// Register a get route.
31    pub fn get<F: Route>(mut self, path: &str, f: F) -> Self {
32        self.routes.insert(path.to_string(), Box::new(f));
33        self
34    }
35
36    /// Starts the server. Blocks until the server quits.
37    /// Can panic if cannot bind the server.
38    pub fn serve(self, addr: SocketAddr) {
39        Server::serve(
40            RequestProcessor {
41                routes: self.routes,
42            },
43            addr,
44        )
45        .run();
46    }
47}
48
49impl From<Request> for Value {
50    fn from(value: Request) -> Self {
51        value.0.body().to_owned()
52    }
53}
54
55impl From<Value> for Response {
56    fn from(value: Value) -> Self {
57        Response(http::Response::new(value))
58    }
59}
60
61impl From<http::StatusCode> for Response {
62    fn from(value: http::StatusCode) -> Self {
63        Response(
64            http::Response::builder()
65                .status(value)
66                .body(Value::Null)
67                .expect("Statically built body should not fail"),
68        )
69    }
70}
71
72pub(crate) struct RequestProcessor {
73    routes: HashMap<String, Box<dyn Route>>,
74}
75
76impl RequestProcessor {
77    pub(crate) fn process(&self, req: Request) -> Response {
78        let Some(route) = self.routes.get(req.0.uri().path()) else {
79            return StatusCode::NOT_FOUND.into();
80        };
81        trace!("Received {:?}", req.0);
82        let res = catch_unwind(AssertUnwindSafe(|| route(req)));
83        let resp = res.unwrap_or(StatusCode::INTERNAL_SERVER_ERROR.into());
84        trace!("Sending {:?}", resp.0);
85        resp
86    }
87}
88
89impl Request {
90    /// Returns the query param by name.
91    pub fn query_param(&self, name: &str) -> Option<String> {
92        let q = self.0.uri().query()?;
93        let f = format!("{name}=");
94        let idx = q.find(&f)?;
95        let end = q[idx..].find('&').unwrap_or(q.len());
96        Some(q[idx + f.len()..end].to_string())
97    }
98}