///|
priv struct Router {
tree : Trie[Route]
}
///|
pub suberror RouterError {
/// Raised when route registration fails.
RouteConflict(path~ : StringView)
}
///|
priv struct Route {
mut get : Handler?
mut post : Handler?
other : Array[(@http.RequestMethod, Handler)]
mut fallback : Handler?
}
///|
fn Route::new() -> Route {
{ get: None, post: None, other: [], fallback: None }
}
///|
fn Route::handler_for(self : Route, meth : @http.RequestMethod) -> Handler? {
match meth {
Get =>
match self.get {
Some(handler) => Some(handler)
None => self.fallback
}
Post =>
match self.post {
Some(handler) => Some(handler)
None => self.fallback
}
_ => {
for pair in self.other {
let (candidate, handler) = pair
if candidate == meth {
return Some(handler)
}
}
self.fallback
}
}
}
///|
fn Route::add_handler(
self : Route,
meth : @http.RequestMethod,
handler : Handler,
) -> Unit raise RouterError {
match meth {
Get =>
match self.get {
Some(_) => raise RouteConflict(path="")
None => self.get = Some(handler)
}
Post =>
match self.post {
Some(_) => raise RouteConflict(path="")
None => self.post = Some(handler)
}
_ => {
for pair in self.other {
let (candidate, _) = pair
if candidate == meth {
raise RouteConflict(path="")
}
}
self.other.push((meth, handler))
}
}
}
///|
fn Route::add_handler_with_path(
self : Route,
meth : @http.RequestMethod,
path : StringView,
handler : Handler,
) -> Unit raise RouterError {
self.add_handler(meth, handler) catch {
RouteConflict(_) => raise RouteConflict(path~)
}
}
///|
fn Route::add_fallback(
self : Route,
handler : Handler,
) -> Unit raise RouterError {
match self.fallback {
Some(_) => raise RouteConflict(path="")
None => self.fallback = Some(handler)
}
}
///|
fn Route::add_fallback_with_path(
self : Route,
path : StringView,
handler : Handler,
) -> Unit raise RouterError {
self.add_fallback(handler) catch {
RouteConflict(_) => raise RouteConflict(path~)
}
}
///|
fn Router::new() -> Router {
{ tree: Trie::root() }
}
///|
fn Router::route_for_path(
self : Router,
path : StringView,
) -> (Route, Map[StringView, StringView])? {
match self.tree.search(path) {
None => None
Some({ payload, params }) => Some((payload, params))
}
}
///|
fn Router::put_route(
self : Router,
path : StringView,
update : (Route) -> Unit raise RouterError,
) -> Unit raise RouterError {
let route = match self.route_for_path(path) {
Some((route, _)) => route
None => Route::new()
}
update(route)
self.tree.insert(path, route)
}
///|
fn Router::add_route(
self : Router,
meth : @http.RequestMethod,
path : StringView,
handler : Handler,
) -> Unit raise RouterError {
self.put_route(path, route => route.add_handler_with_path(meth, path, handler))
}
///|
fn Router::add_fallback_route(
self : Router,
path : StringView,
handler : Handler,
) -> Unit raise RouterError {
self.put_route(path, route => route.add_fallback_with_path(path, handler))
}
///|
priv struct RouteResult {
handler : Handler
params : Map[StringView, StringView]
}
///|
fn Router::route(
self : Router,
meth : @http.RequestMethod,
path : StringView,
) -> RouteResult? {
match self.route_for_path(path) {
None => None
Some((route, params)) =>
match route.handler_for(meth) {
None => None
Some(handler) => Some({ handler, params })
}
}
}