1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
use std::collections::HashMap;
use std::sync::Arc;


use request::SapperRequest;
use response::SapperResponse;
use handler::SapperHandler;
use app::Result;
use app::Error;
use app::PathParams;
use app::Key;
use hyper::method::Method;

use recognizer::Router as Recognizer;
use recognizer::{Match, Params};

impl Key for PathParams { type Value = Params; }


/// `Router` provides an interface for creating complex routes as middleware
/// for the Iron framework.
pub struct Router {
    // The routers, specialized by method.
    routers: HashMap<Method, Recognizer<Arc<Box<dyn SapperHandler>>>>,
    // Routes that accept any method.
    wildcard: Recognizer<Arc<Box<dyn SapperHandler>>>
}

impl Router {
    pub fn new() -> Router {
	Router {
	    routers: HashMap::new(),
	    wildcard: Recognizer::new()
	}
    }

    pub fn route<S>(&mut self, method: Method,
		       glob: S, handler: Arc<Box<dyn SapperHandler>>) -> &mut Router
    where S: AsRef<str> {
	self.routers.entry(method).or_insert(Recognizer::new())
		    .add(glob.as_ref(), handler);
	self
    }


    fn recognize(&self, method: &Method, path: &str)
		     -> Option<Match<&Arc<Box<dyn SapperHandler>>>> {
	self.routers.get(method).and_then(|router| router.recognize(path).ok())
	    .or(self.wildcard.recognize(path).ok())
    }

    // fn handle_options(&self, path: &str) -> Response {
    //     static METHODS: &'static [method::Method] =
    //         &[method::Get, method::Post, method::Post, method::Put,
    //           method::Delete, method::Head, method::Patch];

    //     // Get all the available methods and return them.
    //     let mut options = vec![];

    //     for method in METHODS.iter() {
    //         self.routers.get(method).map(|router| {
    //             if let Some(_) = router.recognize(path).ok() {
    //                 options.push(method.clone());
    //             }
    //         });
    //     }
    //     // If GET is there, HEAD is also there.
    //     if options.contains(&method::Get) && !options.contains(&method::Head) {
    //         options.push(method::Head);
    //     }

    //     let mut res = Response::with(status::StatusCode::Ok);
    //     res.headers.set(headers::Allow(options));
    //     res
    // }

    // Tests for a match by adding or removing a trailing slash.
    // fn redirect_slash(&self, req : &Request) -> Option<Error> {
    //     let mut url = req.url.clone();
    //     let mut path = url.path.join("/");

    //     if let Some(last_char) = path.chars().last() {
    //         if last_char == '/' {
    //             path.pop();
    //             url.path.pop();
    //         } else {
    //             path.push('/');
    //             url.path.push(String::new());
    //         }
    //     }

    //     self.recognize(&req.method(), &path).and(
    //         Some(Error::new(TrailingSlash,
    //                             (status::MovedPermanently, Redirect(url))))
    //     )
    // }

    pub fn handle_method(&self, req: &mut SapperRequest, path: &str) -> Result<SapperResponse> {
	if let Some(matched) = self.recognize(req.method(), path) {
	    req.ext_mut().insert::<PathParams>(matched.params);
	    matched.handler.handle(req)
	} else {
	    // panic!("router not matched!");
	    // self.redirect_slash(req).and_then(|redirect| Some(Err(redirect)))
	    Err(Error::NotFound)
	}
    }
}