use std::error::Error;
use std::path::{Path, Component};
use iron::prelude::*;
use iron::middleware::Handler;
use iron::{status, Url};
use iron::typemap;
use sequence_trie::SequenceTrie;
use std::fmt;
#[derive(Copy, Clone)]
pub struct OriginalUrl;
impl typemap::Key for OriginalUrl { type Value = Url; }
pub struct Mount {
inner: SequenceTrie<String, Match>
}
struct Match {
handler: Box<Handler>,
length: usize
}
#[derive(Debug)]
pub struct NoMatch;
impl Error for NoMatch {
fn description(&self) -> &'static str { "No Match" }
}
impl fmt::Display for NoMatch {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(self.description())
}
}
impl Mount {
pub fn new() -> Mount {
Mount {
inner: SequenceTrie::new()
}
}
pub fn mount<H: Handler>(&mut self, route: &str, handler: H) -> &mut Mount {
let key: Vec<&str> = Path::new(route).components().flat_map(|c|
match c {
Component::RootDir => None,
c => Some(c.as_os_str().to_str().unwrap())
}
).collect();
let match_length = key.len();
self.inner.insert(key, Match {
handler: Box::new(handler) as Box<Handler>,
length: match_length,
});
self
}
}
impl Handler for Mount {
fn handle(&self, req: &mut Request) -> IronResult<Response> {
let matched = {
let path = req.url.path();
let key = match path.last() {
Some(s) if s.is_empty() => &path[..path.len() - 1],
_ => &path
};
let key: Vec<_> = key.into_iter().map(|s| String::from(*s)).collect();
match self.inner.get_ancestor(&key) {
Some(matched) => matched,
None => return Err(IronError::new(NoMatch, status::NotFound))
}
};
let is_outer_mount = !req.extensions.contains::<OriginalUrl>();
if is_outer_mount {
req.extensions.insert::<OriginalUrl>(req.url.clone());
}
let path = req.url.path()[matched.length..].join("/");
req.url.as_mut().set_path(&path);
let res = matched.handler.handle(req);
req.url = match req.extensions.get::<OriginalUrl>() {
Some(original) => original.clone(),
None => panic!("OriginalUrl unexpectedly removed from req.extensions.")
};
if is_outer_mount {
req.extensions.remove::<OriginalUrl>();
}
res
}
}