use std::convert::AsRef; use outcome::IntoOutcome; use response::{self, Responder}; use request::{self, Request, FromRequest}; use http::hyper::{HyperSetCookie, HyperCookiePair, FreshHyperResponse}; const FLASH_COOKIE_NAME: &'static str = "_flash"; pub struct Flash { name: String, message: String, responder: R, } impl Flash { pub fn new, M: AsRef>(res: R, name: N, msg: M) -> Flash { Flash { name: name.as_ref().to_string(), message: msg.as_ref().to_string(), responder: res, } } pub fn warning>(responder: R, msg: S) -> Flash { Flash::new(responder, "warning", msg) } pub fn success>(responder: R, msg: S) -> Flash { Flash::new(responder, "success", msg) } pub fn error>(responder: R, msg: S) -> Flash { Flash::new(responder, "error", msg) } fn cookie_pair(&self) -> HyperCookiePair { let content = format!("{}{}{}", self.name.len(), self.name, self.message); let mut pair = HyperCookiePair::new(FLASH_COOKIE_NAME.to_string(), content); pair.path = Some("/".to_string()); pair.max_age = Some(300); pair } } impl Responder for Flash { fn respond<'b>(&mut self, mut res: FreshHyperResponse<'b>) -> response::Outcome<'b> { trace_!("Flash: setting message: {}:{}", self.name, self.message); res.headers_mut().set(HyperSetCookie(vec![self.cookie_pair()])); self.responder.respond(res) } } impl Flash<()> { fn named(name: &str, msg: &str) -> Flash<()> { Flash { name: name.to_string(), message: msg.to_string(), responder: (), } } pub fn name(&self) -> &str { self.name.as_str() } pub fn msg(&self) -> &str { self.message.as_str() } } // TODO: Using Flash<()> is ugly. Either create a type FlashMessage = Flash<()> // or create a Flash under request that does this. impl<'r> FromRequest<'r> for Flash<()> { type Error = (); fn from_request(request: &'r Request) -> request::Outcome { trace_!("Flash: attemping to retrieve message."); let r = request.cookies().find(FLASH_COOKIE_NAME).ok_or(()).and_then(|cookie| { // Clear the flash message. trace_!("Flash: retrieving message: {:?}", cookie); request.cookies().remove(FLASH_COOKIE_NAME); // Parse the flash. let content = cookie.pair().1; let (len_str, rest) = match content.find(|c: char| !c.is_digit(10)) { Some(i) => (&content[..i], &content[i..]), None => (content, ""), }; let name_len: usize = len_str.parse().map_err(|_| ())?; let (name, msg) = (&rest[..name_len], &rest[name_len..]); Ok(Flash::named(name, msg)) }); r.into_outcome() } }