use std::rc::Rc; use std::convert::From; use std::marker::PhantomData; use std::collections::HashMap; use actix::Actor; use http::Method; use futures::Stream; use task::Task; use route::{Route, RouteHandler, RouteResult, Frame, FnHandler, StreamHandler}; use payload::Payload; use context::HttpContext; use httprequest::HttpRequest; use httpresponse::HttpResponse; use httpcodes::{HTTPNotFound, HTTPMethodNotAllowed}; /// Http resource /// /// `Resource` is an entry in route table which corresponds to requested URL. /// /// Resource in turn has at least one route. /// Route corresponds to handling HTTP method by calling route handler. /// /// ```rust,ignore /// /// struct MyRoute; /// /// fn main() { /// let router = RoutingMap::default() /// .resource("/", |r| r.post::()) /// .finish(); /// } pub struct Resource { name: String, state: PhantomData, routes: HashMap>>, default: Box>, } impl Default for Resource { fn default() -> Self { Resource { name: String::new(), state: PhantomData, routes: HashMap::new(), default: Box::new(HTTPMethodNotAllowed)} } } impl Resource where S: 'static { pub(crate) fn default_not_found() -> Self { Resource { name: String::new(), state: PhantomData, routes: HashMap::new(), default: Box::new(HTTPNotFound)} } /// Set resource name pub fn set_name(&mut self, name: T) { self.name = name.to_string(); } /// Register handler for specified method. pub fn handler(&mut self, method: Method, handler: F) where F: Fn(&mut HttpRequest, Payload, &S) -> R + 'static, R: Into + 'static, { self.routes.insert(method, Box::new(FnHandler::new(handler))); } /// Register async handler for specified method. pub fn async(&mut self, method: Method, handler: F) where F: Fn(&mut HttpRequest, Payload, &S) -> R + 'static, R: Stream + 'static, { self.routes.insert(method, Box::new(StreamHandler::new(handler))); } /// Register handler for specified method. pub fn route_handler(&mut self, method: Method, handler: H) where H: RouteHandler { self.routes.insert(method, Box::new(handler)); } /// Default handler is used if no matched route found. /// By default `HTTPMethodNotAllowed` is used. pub fn default_handler(&mut self, handler: H) where H: RouteHandler { self.default = Box::new(handler); } /// Handler for `GET` method. pub fn get(&mut self) where A: Actor> + Route { self.route_handler(Method::GET, A::factory()); } /// Handler for `POST` method. pub fn post(&mut self) where A: Actor> + Route { self.route_handler(Method::POST, A::factory()); } /// Handler for `PUR` method. pub fn put(&mut self) where A: Actor> + Route { self.route_handler(Method::PUT, A::factory()); } /// Handler for `METHOD` method. pub fn delete(&mut self) where A: Actor> + Route { self.route_handler(Method::DELETE, A::factory()); } } impl RouteHandler for Resource { fn handle(&self, req: &mut HttpRequest, payload: Payload, state: Rc) -> Task { if let Some(handler) = self.routes.get(req.method()) { handler.handle(req, payload, state) } else { self.default.handle(req, payload, state) } } } #[cfg_attr(feature="cargo-clippy", allow(large_enum_variant))] enum ReplyItem where A: Actor + Route { Message(HttpResponse), Actor(A), } /// Represents response process. pub struct Reply (ReplyItem); impl Reply where A: Actor + Route { /// Create async response pub fn async(act: A) -> RouteResult { Ok(Reply(ReplyItem::Actor(act))) } /// Send response pub fn reply>(response: R) -> RouteResult { Ok(Reply(ReplyItem::Message(response.into()))) } pub fn into(self, mut ctx: HttpContext) -> Task where A: Actor> { match self.0 { ReplyItem::Message(msg) => { Task::reply(msg) }, ReplyItem::Actor(act) => { ctx.set_actor(act); Task::with_context(ctx) } } } } impl From for Reply where T: Into, A: Actor + Route { fn from(item: T) -> Self { Reply(ReplyItem::Message(item.into())) } }