pub(crate) mod matcher;
#[cfg(test)]
mod tests;
use std::collections::HashMap;
use std::sync::Arc;
use crate::request::Request;
use crate::response::Response;
use crate::server::ConnectionInfo;
use matcher::Segment;
pub struct PathParams {
params: HashMap<String, String>,
}
impl PathParams {
pub(crate) fn from_map(params: HashMap<String, String>) -> Self {
PathParams { params }
}
pub fn get(&self, name: &str) -> Option<&str> {
self.params.get(name).map(String::as_str)
}
}
type HandlerFn =
Arc<dyn Fn(&Request, &PathParams, &ConnectionInfo) -> Response + Send + Sync + 'static>;
#[derive(Clone)]
struct Route {
method: String,
segments: Vec<Segment>,
handler: HandlerFn,
}
#[derive(Clone)]
pub struct RouteInfo {
pub method: String,
pub pattern: String,
}
#[derive(Clone)]
pub struct Router {
routes: Vec<Route>,
host: Option<String>,
}
impl Router {
pub fn new() -> Self {
Router { routes: Vec::new(), host: None }
}
pub fn with_host(mut self, host: &str) -> Self {
self.host = Some(host.to_string());
self
}
pub fn get<F>(self, pattern: &str, handler: F) -> Self
where F: Fn(&Request, &PathParams, &ConnectionInfo) -> Response + Send + Sync + 'static {
self.add("GET", pattern, handler)
}
pub fn post<F>(self, pattern: &str, handler: F) -> Self
where F: Fn(&Request, &PathParams, &ConnectionInfo) -> Response + Send + Sync + 'static {
self.add("POST", pattern, handler)
}
pub fn put<F>(self, pattern: &str, handler: F) -> Self
where F: Fn(&Request, &PathParams, &ConnectionInfo) -> Response + Send + Sync + 'static {
self.add("PUT", pattern, handler)
}
pub fn patch<F>(self, pattern: &str, handler: F) -> Self
where F: Fn(&Request, &PathParams, &ConnectionInfo) -> Response + Send + Sync + 'static {
self.add("PATCH", pattern, handler)
}
pub fn delete<F>(self, pattern: &str, handler: F) -> Self
where F: Fn(&Request, &PathParams, &ConnectionInfo) -> Response + Send + Sync + 'static {
self.add("DELETE", pattern, handler)
}
fn add<F>(mut self, method: &str, pattern: &str, handler: F) -> Self
where F: Fn(&Request, &PathParams, &ConnectionInfo) -> Response + Send + Sync + 'static {
self.routes.push(Route {
method: method.to_string(),
segments: matcher::parse_pattern(pattern),
handler: Arc::new(handler),
});
self
}
pub fn route_entries(&self) -> Vec<RouteInfo> {
self.routes.iter().map(|r| RouteInfo {
method: r.method.clone(),
pattern: matcher::segments_to_pattern(&r.segments),
}).collect()
}
pub fn handle(&self, request: &Request, connection: &ConnectionInfo) -> Option<Response> {
if let Some(required_host) = &self.host {
let actual = connection.sni_hostname.as_deref().or_else(|| {
request.headers.iter()
.find(|h| h.name.eq_ignore_ascii_case("host"))
.map(|h| h.value.as_str())
});
if actual != Some(required_host.as_str()) {
return None;
}
}
let path = request.request_uri.split('?').next().unwrap_or(&request.request_uri);
let path_segs: Vec<&str> = path.split('/').filter(|s| !s.is_empty()).collect();
for route in &self.routes {
if route.method != request.method {
continue;
}
if let Some(params) = matcher::try_match(&route.segments, &path_segs) {
let params = PathParams::from_map(params);
return Some((route.handler)(request, ¶ms, connection));
}
}
None
}
}