extern crate iron;
use iron::prelude::*;
use iron::status;
use iron::request::Body;
use std::collections::HashMap;
use std::hash::{Hash, Hasher};
use std::cmp::{PartialEq, Eq};
use std::option::Option::None;
#[cfg(test)]
mod tests {
use Router;
use std::collections::HashMap;
use iron::prelude::*;
use iron::status;
use iron::request::Body;
use std::io::Read;
fn test_handle(vars: HashMap<String, String>, body: &mut Body) -> IronResult<Response>
{
let mut string = "Vars:".to_owned();
for (x, y) in &vars {
string.push_str(&format!("\n{} -> {}", x, y));
}
string.push_str("\n");
let mut buf: String = "".to_owned();
let res = body.read_to_string(&mut buf);
string.push_str(&buf[..]);
Ok (
Response::with((status::Ok, string))
)
}
#[test]
fn test_router()
{
let mut r = Router::new();
r.get("/id/:album", test_handle);
let handle = move |_req: &mut Request| -> IronResult<Response> {
return r.handle(_req);
};
}
}
pub struct MatchableUrlResult {
matches: bool,
variables: Option<HashMap<String, String>>
}
pub struct MatchableUrl {
url: String,
components: Vec<(String, bool)>
}
impl Hash for MatchableUrl {
fn hash<H: Hasher>(&self, state: &mut H) {
self.url.hash(state);
self.components.hash(state);
}
}
impl PartialEq for MatchableUrl {
fn eq(&self, other: &MatchableUrl) -> bool {
return self.url == other.url;
}
}
impl Eq for MatchableUrl {}
impl MatchableUrl {
pub fn new(url: &str) -> MatchableUrl
{
let split = url[1..].split("/");
let mut comp : Vec<(String, bool)> = Vec::new();
for s in split {
if s.find(":") == None {
comp.push((s.to_owned(), true));
} else {
comp.push((s.to_owned(), false));
}
}
return MatchableUrl { url: url.to_owned(), components: comp };
}
pub fn _match(&self, request_path: Vec<&str>) -> MatchableUrlResult
{
let size = request_path.len();
if size != self.components.len() {
return MatchableUrlResult { matches: false, variables: None }
}
let mut index = 0;
let mut vars : HashMap<String, String> = HashMap::new();
for s in request_path {
let (ref name, ref exac) = self.components[index];
if *exac {
if name != s {
return MatchableUrlResult { matches: false, variables: None }
}
} else {
vars.insert(name[1..].to_owned(), s.to_owned());
}
index += 1;
}
return MatchableUrlResult { matches: true, variables: Some(vars) };
}
}
pub struct Router {
routes: HashMap<MatchableUrl, fn(HashMap<String, String>, &mut Body) -> IronResult<Response>>
}
impl Router {
pub fn new() -> Router
{
return Router { routes: HashMap::new() };
}
pub fn handle(&self, _req: &mut Request) -> IronResult<Response>
{
for (m_url, f) in &self.routes {
let path = _req.url.path();
let res = m_url._match(path);
let b = &mut _req.body;
if res.matches {
return f(res.variables.unwrap(), b);
}
}
Ok(
Response::with((status:: Ok, "No match."))
)
}
pub fn get(&mut self, path: &str, f: fn(HashMap<String, String>, &mut Body) -> IronResult<Response>)
{
let url = MatchableUrl::new(path);
self.routes.insert(url, f);
}
}