1use crate::{
2 routes::{RequestProcessor, RouteContext, RouteMethod},
3 server::Server,
4};
5use serde_json::Value;
6use std::{collections::HashMap, net::SocketAddr};
7
8pub struct Request(pub http::Request<Value>);
9pub struct Response(pub http::Response<Value>);
10
11pub trait Route: FnMut(Request) -> Response + 'static {}
13impl<T> Route for T where T: FnMut(Request) -> Response + 'static {}
14
15pub trait TextRoute: FnMut() -> String + 'static {}
21impl<T> TextRoute for T where T: FnMut() -> String + 'static {}
22
23#[derive(Default)]
42pub struct Mise {
43 routes: HashMap<RouteMethod, HashMap<String, RouteContext>>,
44 text_routes: HashMap<String, Box<dyn TextRoute>>,
45}
46
47impl Mise {
48 pub fn new() -> Self {
50 Self::default()
51 }
52
53 pub fn text<F: TextRoute>(mut self, path: &str, f: F) -> Self {
58 self.text_routes.insert(path.to_string(), Box::new(f));
59 self
60 }
61
62 pub fn get<F: Route>(mut self, path: &str, f: F) -> Self {
64 let routes = self.routes.entry(RouteMethod::Get).or_default();
65 let d: Box<dyn Route> = Box::new(f);
66 routes.insert(path.to_string(), (path, d).into());
67 self
68 }
69
70 pub fn post<F: Route>(mut self, path: &str, f: F) -> Self {
73 let routes = self.routes.entry(RouteMethod::Post).or_default();
74 let d: Box<dyn Route> = Box::new(f);
75 routes.insert(path.to_string(), (path, d).into());
76 self
77 }
78
79 pub fn serve(self, addr: SocketAddr) {
82 Server::serve(
83 RequestProcessor {
84 routes: self.routes,
85 text_routes: self.text_routes,
86 },
87 addr,
88 )
89 .run();
90 }
91}
92
93impl From<Request> for Value {
94 fn from(value: Request) -> Self {
95 value.0.body().to_owned()
96 }
97}
98
99impl From<Value> for Response {
100 fn from(value: Value) -> Self {
101 Response(http::Response::new(value))
102 }
103}
104
105impl From<http::StatusCode> for Response {
106 fn from(value: http::StatusCode) -> Self {
107 Response(
108 http::Response::builder()
109 .status(value)
110 .body(Value::Null)
111 .expect("Statically built body should not fail"),
112 )
113 }
114}
115
116impl Request {
117 pub fn path(&self) -> &str {
119 self.0.uri().path()
120 }
121
122 pub fn query(&self) -> Option<&str> {
123 self.0.uri().query()
124 }
125
126 pub fn query_param(&self, name: &str) -> Option<&str> {
128 let q = self.0.uri().query()?;
129 let f = format!("{name}=");
130 let idx = q.find(&f)?;
131 let end = q[idx..].find('&').unwrap_or(q.len());
132 Some(&q[idx + f.len()..end])
133 }
134
135 pub fn base(&self) -> &str {
141 let p = self.0.uri().path();
142 let n = self.name();
143 p[..p.len() - n.len()].trim_end_matches("/")
144 }
145
146 pub fn name(&self) -> &str {
152 if !self.0.uri().path().contains("/") {
153 return "";
154 }
155 self.0.uri().path().split("/").last().unwrap_or("")
156 }
157
158 pub fn body(&self) -> &Value {
162 self.0.body()
163 }
164
165 pub(crate) fn base_star(&self) -> Option<String> {
166 if self.name().is_empty() {
167 return None;
169 }
170 Some(format!("{}/*", self.base()))
171 }
172}
173
174#[cfg(test)]
175mod test {
176 use super::*;
177
178 #[test]
179 fn test_paths() {
180 assert_eq!(requri("/").name(), "");
181 assert_eq!(requri("/a").name(), "a");
182 assert_eq!(requri("/a/b").name(), "b");
183
184 assert_eq!(requri("/").base(), "");
185 assert_eq!(requri("/a").base(), "");
186 assert_eq!(requri("/a/b").base(), "/a");
187
188 assert_eq!(requri("/").base_star(), None);
189 assert_eq!(requri("/a").base_star(), Some("/*".to_string()));
190 assert_eq!(requri("/a/b").base_star(), Some("/a/*".to_string()));
191
192 assert_eq!(requri("/?b=a").query_param("b"), Some("a"));
193 }
194
195 fn requri(uri: &str) -> Request {
196 Request(http::Request::builder().uri(uri).body(Value::Null).unwrap())
197 }
198}