//! Provides the [`SputnikBuilder`] trait. use std::convert::TryInto; use cookie::Cookie; use hyper::{HeaderMap, StatusCode, header, http}; use time::{Duration, OffsetDateTime}; use hyper::http::response::Builder; pub trait SputnikBuilder { /// Sets the Content-Type. fn content_type(self, mime: mime::Mime) -> Builder; } pub fn redirect(location: &str, code: StatusCode) -> Builder { Builder::new().status(code).header(header::LOCATION, location) } impl SputnikBuilder for Builder { fn content_type(mut self, mime: mime::Mime) -> Self { if let Some(headers) = self.headers_mut() { headers.insert(header::CONTENT_TYPE, mime.to_string().try_into().unwrap()); } self } } pub trait SputnikHeaders { /// Appends a Set-Cookie header. fn set_cookie(&mut self, cookie: Cookie); /// Appends a Set-Cookie header to delete a cookie. fn delete_cookie(&mut self, name: &str); } impl SputnikHeaders for HeaderMap { fn set_cookie(&mut self, cookie: Cookie) { self.append(header::SET_COOKIE, cookie.encoded().to_string().try_into().unwrap()); } fn delete_cookie(&mut self, name: &str) { let mut cookie = Cookie::new(name, ""); cookie.set_max_age(Duration::seconds(0)); cookie.set_expires(OffsetDateTime::now_utc() - Duration::days(365)); self.set_cookie(cookie); } } pub trait EmptyBuilder { /// Consume the builder with an empty body. fn empty(self) -> http::Result>; } impl EmptyBuilder for Builder { fn empty(self) -> http::Result> { self.body(hyper::Body::empty()) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_set_cookie() { let mut map = HeaderMap::new(); map.set_cookie(Cookie::new("some", "cookie")); map.set_cookie(Cookie::new("some", "cookie")); assert_eq!(map.len(), 2); } #[test] fn test_content_type() { let resp = Builder::new() .content_type(mime::TEXT_PLAIN) .content_type(mime::TEXT_HTML) .body(hyper::Body::empty()).unwrap(); assert_eq!(resp.headers().len(), 1); assert_eq!(resp.headers().get(header::CONTENT_TYPE).unwrap(), "text/html"); // doesn't panic after invalid header Builder::new() .header("foo", "invalid\r\n") .content_type(mime::TEXT_HTML); } }