refactor(header): change Header::fmt_header to take a header::Formatter

The `header::Formatter` ensures that a formatted header is written to a
line, and allows for headers that require multiple lines. The only
header to specifically require this is `Set-Cookie`.

BREAKING CHANGE: The `fmt_header` method has changed to take a different
  formatter. In most cases, if your header also implements
  `fmt::Display`, you can just call `f.fmt_line(self)`.
This commit is contained in:
Sean McArthur
2017-04-20 23:01:25 -07:00
parent f1859dfd7a
commit 6f02d43ae0
24 changed files with 177 additions and 162 deletions

View File

@@ -63,14 +63,14 @@ impl Header for AccessControlAllowCredentials {
Err(::Error::Header) Err(::Error::Header)
} }
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt_header(&self, f: &mut ::header::Formatter) -> fmt::Result {
f.write_str("true") f.fmt_line(self)
} }
} }
impl Display for AccessControlAllowCredentials { impl Display for AccessControlAllowCredentials {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
self.fmt_header(f) f.write_str("true")
} }
} }

View File

@@ -72,18 +72,18 @@ impl Header for AccessControlAllowOrigin {
} }
} }
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt_header(&self, f: &mut ::header::Formatter) -> fmt::Result {
match *self { f.fmt_line(self)
AccessControlAllowOrigin::Any => f.write_str("*"),
AccessControlAllowOrigin::Null => f.write_str("null"),
AccessControlAllowOrigin::Value(ref url) => Display::fmt(url, f),
}
} }
} }
impl Display for AccessControlAllowOrigin { impl Display for AccessControlAllowOrigin {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
self.fmt_header(f) match *self {
AccessControlAllowOrigin::Any => f.write_str("*"),
AccessControlAllowOrigin::Null => f.write_str("null"),
AccessControlAllowOrigin::Value(ref url) => Display::fmt(url, f),
}
} }
} }

View File

@@ -100,8 +100,8 @@ impl<S: Scheme + Any> Header for Authorization<S> where <S as FromStr>::Err: 'st
} }
} }
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt_header(&self, f: &mut ::header::Formatter) -> fmt::Result {
fmt::Display::fmt(self, f) f.fmt_line(self)
} }
} }

View File

@@ -49,6 +49,7 @@ pub struct CacheControl(pub Vec<CacheDirective>);
__hyper__deref!(CacheControl => Vec<CacheDirective>); __hyper__deref!(CacheControl => Vec<CacheDirective>);
//TODO: this could just be the header! macro
impl Header for CacheControl { impl Header for CacheControl {
fn header_name() -> &'static str { fn header_name() -> &'static str {
static NAME: &'static str = "Cache-Control"; static NAME: &'static str = "Cache-Control";
@@ -64,8 +65,8 @@ impl Header for CacheControl {
} }
} }
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt_header(&self, f: &mut ::header::Formatter) -> fmt::Result {
fmt::Display::fmt(self, f) f.fmt_line(self)
} }
} }

View File

@@ -146,8 +146,8 @@ impl Header for ContentDisposition {
} }
#[inline] #[inline]
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt_header(&self, f: &mut ::header::Formatter) -> fmt::Result {
fmt::Display::fmt(&self, f) f.fmt_line(self)
} }
} }

View File

@@ -60,8 +60,8 @@ impl Header for ContentLength {
} }
#[inline] #[inline]
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt_header(&self, f: &mut ::header::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.0, f) f.fmt_line(self)
} }
} }

View File

@@ -54,8 +54,8 @@ impl Header for Cookie {
} }
} }
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt_header(&self, f: &mut ::header::Formatter) -> fmt::Result {
fmt::Display::fmt(self, f) f.fmt_line(self)
} }
} }

View File

@@ -54,8 +54,8 @@ impl Header for Expect {
} }
} }
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt_header(&self, f: &mut ::header::Formatter) -> fmt::Result {
fmt::Display::fmt(self, f) f.fmt_line(self)
} }
} }

View File

@@ -68,17 +68,17 @@ impl Header for Host {
from_one_raw_str(raw) from_one_raw_str(raw)
} }
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt_header(&self, f: &mut ::header::Formatter) -> fmt::Result {
match self.port { f.fmt_line(self)
None | Some(80) | Some(443) => f.write_str(&self.hostname[..]),
Some(port) => write!(f, "{}:{}", self.hostname, port)
}
} }
} }
impl fmt::Display for Host { impl fmt::Display for Host {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.fmt_header(f) match self.port {
None | Some(80) | Some(443) => f.write_str(&self.hostname[..]),
Some(port) => write!(f, "{}:{}", self.hostname, port)
}
} }
} }

View File

@@ -70,17 +70,17 @@ impl Header for IfRange {
Err(::Error::Header) Err(::Error::Header)
} }
fn fmt_header(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fn fmt_header(&self, f: &mut ::header::Formatter) -> ::std::fmt::Result {
match *self { f.fmt_line(self)
IfRange::EntityTag(ref x) => Display::fmt(x, f),
IfRange::Date(ref x) => Display::fmt(x, f),
}
} }
} }
impl Display for IfRange { impl Display for IfRange {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.fmt_header(f) match *self {
IfRange::EntityTag(ref x) => Display::fmt(x, f),
IfRange::Date(ref x) => Display::fmt(x, f),
}
} }
} }

View File

@@ -423,14 +423,14 @@ impl Header for Link {
.unwrap_or(Err(::Error::Header)) .unwrap_or(Err(::Error::Header))
} }
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt_header(&self, f: &mut ::header::Formatter) -> fmt::Result {
fmt_delimited(f, self.values.as_slice(), ", ", ("", "")) f.fmt_line(self)
} }
} }
impl fmt::Display for Link { impl fmt::Display for Link {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.fmt_header(f) fmt_delimited(f, self.values.as_slice(), ", ", ("", ""))
} }
} }

View File

@@ -204,14 +204,13 @@ macro_rules! header {
fn parse_header(raw: &$crate::header::Raw) -> $crate::Result<Self> { fn parse_header(raw: &$crate::header::Raw) -> $crate::Result<Self> {
$crate::header::parsing::from_comma_delimited(raw).map($id) $crate::header::parsing::from_comma_delimited(raw).map($id)
} }
fn fmt_header(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fn fmt_header(&self, f: &mut $crate::header::Formatter) -> ::std::fmt::Result {
$crate::header::parsing::fmt_comma_delimited(f, &self.0[..]) f.fmt_line(self)
} }
} }
impl ::std::fmt::Display for $id { impl ::std::fmt::Display for $id {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
use $crate::header::Header; $crate::header::parsing::fmt_comma_delimited(f, &self.0[..])
self.fmt_header(f)
} }
} }
}; };
@@ -229,14 +228,13 @@ macro_rules! header {
fn parse_header(raw: &$crate::header::Raw) -> $crate::Result<Self> { fn parse_header(raw: &$crate::header::Raw) -> $crate::Result<Self> {
$crate::header::parsing::from_comma_delimited(raw).map($id) $crate::header::parsing::from_comma_delimited(raw).map($id)
} }
fn fmt_header(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fn fmt_header(&self, f: &mut $crate::header::Formatter) -> ::std::fmt::Result {
$crate::header::parsing::fmt_comma_delimited(f, &self.0[..]) f.fmt_line(self)
} }
} }
impl ::std::fmt::Display for $id { impl ::std::fmt::Display for $id {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
use $crate::header::Header; $crate::header::parsing::fmt_comma_delimited(f, &self.0[..])
self.fmt_header(f)
} }
} }
}; };
@@ -254,8 +252,8 @@ macro_rules! header {
fn parse_header(raw: &$crate::header::Raw) -> $crate::Result<Self> { fn parse_header(raw: &$crate::header::Raw) -> $crate::Result<Self> {
$crate::header::parsing::from_one_raw_str(raw).map($id) $crate::header::parsing::from_one_raw_str(raw).map($id)
} }
fn fmt_header(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fn fmt_header(&self, f: &mut $crate::header::Formatter) -> ::std::fmt::Result {
::std::fmt::Display::fmt(&**self, f) f.fmt_line(self)
} }
} }
impl ::std::fmt::Display for $id { impl ::std::fmt::Display for $id {
@@ -289,8 +287,8 @@ macro_rules! header {
fn parse_header(raw: &$crate::header::Raw) -> $crate::Result<Self> { fn parse_header(raw: &$crate::header::Raw) -> $crate::Result<Self> {
$crate::header::parsing::from_one_raw_str::<<$value as ::std::borrow::ToOwned>::Owned>(raw).map($id::new) $crate::header::parsing::from_one_raw_str::<<$value as ::std::borrow::ToOwned>::Owned>(raw).map($id::new)
} }
fn fmt_header(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fn fmt_header(&self, f: &mut $crate::header::Formatter) -> ::std::fmt::Result {
::std::fmt::Display::fmt(&**self, f) f.fmt_line(self)
} }
} }
impl ::std::fmt::Display for $id { impl ::std::fmt::Display for $id {
@@ -323,7 +321,12 @@ macro_rules! header {
} }
$crate::header::parsing::from_comma_delimited(raw).map($id::Items) $crate::header::parsing::from_comma_delimited(raw).map($id::Items)
} }
fn fmt_header(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { fn fmt_header(&self, f: &mut $crate::header::Formatter) -> ::std::fmt::Result {
f.fmt_line(self)
}
}
impl ::std::fmt::Display for $id {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
match *self { match *self {
$id::Any => f.write_str("*"), $id::Any => f.write_str("*"),
$id::Items(ref fields) => $crate::header::parsing::fmt_comma_delimited( $id::Items(ref fields) => $crate::header::parsing::fmt_comma_delimited(
@@ -331,12 +334,6 @@ macro_rules! header {
} }
} }
} }
impl ::std::fmt::Display for $id {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
use $crate::header::Header;
self.fmt_header(f)
}
}
}; };
// optional test module // optional test module

View File

@@ -104,8 +104,8 @@ impl Header for Origin {
from_one_raw_str(raw) from_one_raw_str(raw)
} }
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt_header(&self, f: &mut ::header::Formatter) -> fmt::Result {
fmt::Display::fmt(self, f) f.fmt_line(self)
} }
} }

View File

@@ -54,8 +54,8 @@ impl Header for Pragma {
}) })
} }
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt_header(&self, f: &mut ::header::Formatter) -> fmt::Result {
fmt::Display::fmt(self, f) f.fmt_line(self)
} }
} }

View File

@@ -65,8 +65,8 @@ impl Header for Prefer {
} }
} }
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt_header(&self, f: &mut ::header::Formatter) -> fmt::Result {
fmt::Display::fmt(self, f) f.fmt_line(self)
} }
} }

View File

@@ -63,8 +63,8 @@ impl Header for PreferenceApplied {
} }
} }
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt_header(&self, f: &mut ::header::Formatter) -> fmt::Result {
fmt::Display::fmt(self, f) f.fmt_line(self)
} }
} }

View File

@@ -190,8 +190,8 @@ impl Header for Range {
from_one_raw_str(raw) from_one_raw_str(raw)
} }
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt_header(&self, f: &mut ::header::Formatter) -> fmt::Result {
Display::fmt(self, f) f.fmt_line(self)
} }
} }

View File

@@ -79,8 +79,8 @@ impl Header for ReferrerPolicy {
Err(::Error::Header) Err(::Error::Header)
} }
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt_header(&self, f: &mut ::header::Formatter) -> fmt::Result {
fmt::Display::fmt(self, f) f.fmt_line(self)
} }
} }

View File

@@ -121,7 +121,13 @@ impl Header for RetryAfter {
} }
} }
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt_header(&self, f: &mut ::header::Formatter) -> ::std::fmt::Result {
f.fmt_line(self)
}
}
impl fmt::Display for RetryAfter {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self { match *self {
RetryAfter::Delay(ref duration) => { RetryAfter::Delay(ref duration) => {
write!(f, "{}", duration.num_seconds()) write!(f, "{}", duration.num_seconds())

View File

@@ -91,16 +91,7 @@ impl Header for SetCookie {
} }
} }
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt_header(&self, f: &mut ::header::Formatter) -> fmt::Result {
if self.0.len() == 1 {
write!(f, "{}", &self.0[0])
} else {
panic!("SetCookie with multiple cookies cannot be used with fmt_header, must use fmt_multi_header");
}
}
fn fmt_multi_header(&self, f: &mut ::header::MultilineFormatter) -> fmt::Result {
for cookie in &self.0 { for cookie in &self.0 {
try!(f.fmt_line(cookie)); try!(f.fmt_line(cookie));
} }

View File

@@ -129,8 +129,8 @@ impl Header for StrictTransportSecurity {
parsing::from_one_raw_str(raw) parsing::from_one_raw_str(raw)
} }
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt_header(&self, f: &mut ::header::Formatter) -> fmt::Result {
fmt::Display::fmt(self, f) f.fmt_line(self)
} }
} }

View File

@@ -96,7 +96,13 @@ impl Header for Warning {
from_one_raw_str(raw) from_one_raw_str(raw)
} }
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt_header(&self, f: &mut ::header::Formatter) -> fmt::Result {
f.fmt_line(self)
}
}
impl fmt::Display for Warning {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.date { match self.date {
Some(date) => write!(f, "{:03} {} \"{}\" \"{}\"", self.code, self.agent, self.text, date), Some(date) => write!(f, "{:03} {} \"{}\" \"{}\"", self.code, self.agent, self.text, date),
None => write!(f, "{:03} {} \"{}\"", self.code, self.agent, self.text) None => write!(f, "{:03} {} \"{}\"", self.code, self.agent, self.text)

View File

@@ -4,7 +4,7 @@ use std::fmt;
use std::str::from_utf8; use std::str::from_utf8;
use super::cell::{OptCell, PtrMapCell}; use super::cell::{OptCell, PtrMapCell};
use header::{Header, MultilineFormatter, Multi, raw, Raw}; use header::{Header, Formatter, Multi, raw, Raw};
#[derive(Clone)] #[derive(Clone)]
@@ -47,7 +47,7 @@ impl Item {
} }
let mut raw = raw::new(); let mut raw = raw::new();
self.write_h1(&mut MultilineFormatter(Multi::Raw(&mut raw))).expect("fmt failed"); self.write_h1(&mut Formatter(Multi::Raw(&mut raw))).expect("fmt failed");
self.raw.set(raw); self.raw.set(raw);
self.raw.as_ref().unwrap() self.raw.as_ref().unwrap()
@@ -93,7 +93,7 @@ impl Item {
}.map(|typed| unsafe { typed.downcast_unchecked() }) }.map(|typed| unsafe { typed.downcast_unchecked() })
} }
pub fn write_h1(&self, f: &mut MultilineFormatter) -> fmt::Result { pub fn write_h1(&self, f: &mut Formatter) -> fmt::Result {
match *self.raw { match *self.raw {
Some(ref raw) => { Some(ref raw) => {
for part in raw.iter() { for part in raw.iter() {
@@ -111,7 +111,7 @@ impl Item {
}, },
None => { None => {
let typed = unsafe { self.typed.one() }; let typed = unsafe { self.typed.one() };
typed.fmt_multi_header(f) typed.fmt_header(f)
} }
} }
} }

View File

@@ -41,7 +41,7 @@
//! //!
//! ``` //! ```
//! use std::fmt; //! use std::fmt;
//! use hyper::header::{Header, Raw}; //! use hyper::header::{self, Header, Raw};
//! //!
//! #[derive(Debug, Clone, Copy)] //! #[derive(Debug, Clone, Copy)]
//! struct Dnt(bool); //! struct Dnt(bool);
@@ -66,16 +66,16 @@
//! Err(hyper::Error::Header) //! Err(hyper::Error::Header)
//! } //! }
//! //!
//! fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { //! fn fmt_header(&self, f: &mut header::Formatter) -> fmt::Result {
//! if self.0 { //! let value = if self.0 {
//! f.write_str("1") //! "1"
//! } else { //! } else {
//! f.write_str("0") //! "0"
//! } //! };
//! f.fmt_line(&value)
//! } //! }
//! } //! }
//! ``` //! ```
use std::any::{Any, TypeId};
use std::borrow::{Cow, ToOwned}; use std::borrow::{Cow, ToOwned};
use std::iter::{FromIterator, IntoIterator}; use std::iter::{FromIterator, IntoIterator};
use std::{mem, fmt}; use std::{mem, fmt};
@@ -83,6 +83,7 @@ use std::{mem, fmt};
use unicase::UniCase; use unicase::UniCase;
use self::internals::{Item, VecMap, Entry}; use self::internals::{Item, VecMap, Entry};
use self::sealed::{GetType, HeaderClone};
pub use self::shared::*; pub use self::shared::*;
pub use self::common::*; pub use self::common::*;
@@ -100,7 +101,7 @@ pub mod parsing;
/// ///
/// This trait represents the construction and identification of headers, /// This trait represents the construction and identification of headers,
/// and contains trait-object unsafe methods. /// and contains trait-object unsafe methods.
pub trait Header: HeaderClone + Any + GetType + Send + Sync { pub trait Header: HeaderClone + GetType + Send + Sync {
/// Returns the name of the header field this belongs to. /// Returns the name of the header field this belongs to.
/// ///
/// This will become an associated constant once available. /// This will become an associated constant once available.
@@ -113,56 +114,71 @@ pub trait Header: HeaderClone + Any + GetType + Send + Sync {
/// than one field value. If that's the case, you **should** return `None` /// than one field value. If that's the case, you **should** return `None`
/// if `raw.len() > 1`. /// if `raw.len() > 1`.
fn parse_header(raw: &Raw) -> ::Result<Self> where Self: Sized; fn parse_header(raw: &Raw) -> ::Result<Self> where Self: Sized;
/// Format a header to be output into a TcpStream. /// Format a header to outgoing stream.
/// ///
/// This method is not allowed to introduce an Err not produced /// Most headers should be formatted on one line, and so a common pattern
/// by the passed-in Formatter. /// would be to implement `std::fmt::Display` for this type as well, and
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result; /// then just call `f.fmt_line(self)`.
/// Formats a header over multiple lines. ///
/// ## Note
///
/// This has the ability to format a header over multiple lines.
/// ///
/// The main example here is `Set-Cookie`, which requires that every /// The main example here is `Set-Cookie`, which requires that every
/// cookie being set be specified in a separate line. /// cookie being set be specified in a separate line. Almost every other
/// /// case should only format as 1 single line.
/// The API here is still being explored, so this is hidden by default.
/// The passed in formatter doesn't have any public methods, so it would
/// be quite difficult to depend on this externally.
#[doc(hidden)]
#[inline] #[inline]
fn fmt_multi_header(&self, f: &mut MultilineFormatter) -> fmt::Result { fn fmt_header(&self, f: &mut Formatter) -> fmt::Result;
f.fmt_line(&FmtHeader(self)) }
mod sealed {
use std::any::{Any, TypeId};
use super::Header;
#[doc(hidden)]
pub trait GetType: Any {
#[inline(always)]
fn get_type(&self) -> TypeId {
TypeId::of::<Self>()
}
}
impl<T: Any> GetType for T {}
#[doc(hidden)]
pub trait HeaderClone {
fn clone_box(&self) -> Box<Header + Send + Sync>;
}
impl<T: Header + Clone> HeaderClone for T {
#[inline]
fn clone_box(&self) -> Box<Header + Send + Sync> {
Box::new(self.clone())
}
}
#[test]
fn test_get_type() {
use ::header::{ContentLength, UserAgent};
let len = ContentLength(5);
let agent = UserAgent::new("hyper");
assert_eq!(TypeId::of::<ContentLength>(), len.get_type());
assert_eq!(TypeId::of::<UserAgent>(), agent.get_type());
let len: Box<Header + Send + Sync> = Box::new(len);
let agent: Box<Header + Send + Sync> = Box::new(agent);
assert_eq!(TypeId::of::<ContentLength>(), (*len).get_type());
assert_eq!(TypeId::of::<UserAgent>(), (*agent).get_type());
} }
} }
#[doc(hidden)]
pub trait GetType: Any {
#[inline(always)]
fn get_type(&self) -> TypeId {
TypeId::of::<Self>()
}
}
impl<T: Any> GetType for T {} /// A formatter used to serialize headers to an output stream.
#[test]
fn test_get_type() {
use ::header::{ContentLength, UserAgent};
let len = ContentLength(5);
let agent = UserAgent::new("hyper");
assert_eq!(TypeId::of::<ContentLength>(), len.get_type());
assert_eq!(TypeId::of::<UserAgent>(), agent.get_type());
let len: Box<Header + Send + Sync> = Box::new(len);
let agent: Box<Header + Send + Sync> = Box::new(agent);
assert_eq!(TypeId::of::<ContentLength>(), (*len).get_type());
assert_eq!(TypeId::of::<UserAgent>(), (*agent).get_type());
}
#[doc(hidden)]
#[allow(missing_debug_implementations)] #[allow(missing_debug_implementations)]
pub struct MultilineFormatter<'a, 'b: 'a>(Multi<'a, 'b>); pub struct Formatter<'a, 'b: 'a>(Multi<'a, 'b>);
enum Multi<'a, 'b: 'a> { enum Multi<'a, 'b: 'a> {
Line(&'a str, &'a mut fmt::Formatter<'b>), Line(&'a str, &'a mut fmt::Formatter<'b>),
@@ -170,8 +186,20 @@ enum Multi<'a, 'b: 'a> {
Raw(&'a mut Raw), Raw(&'a mut Raw),
} }
impl<'a, 'b> MultilineFormatter<'a, 'b> { impl<'a, 'b> Formatter<'a, 'b> {
fn fmt_line(&mut self, line: &fmt::Display) -> fmt::Result {
/// Format one 'line' of a header.
///
/// This writes the header name plus the `Display` value as a single line.
///
/// ## Note
///
/// This has the ability to format a header over multiple lines.
///
/// The main example here is `Set-Cookie`, which requires that every
/// cookie being set be specified in a separate line. Almost every other
/// case should only format as 1 single line.
pub fn fmt_line(&mut self, line: &fmt::Display) -> fmt::Result {
use std::fmt::Write; use std::fmt::Write;
match self.0 { match self.0 {
Multi::Line(ref name, ref mut f) => { Multi::Line(ref name, ref mut f) => {
@@ -198,28 +226,19 @@ impl<'a, 'b> MultilineFormatter<'a, 'b> {
} }
} }
// Internal helper to wrap fmt_header into a fmt::Display
struct FmtHeader<'a, H: ?Sized + 'a>(&'a H);
impl<'a, H: Header + ?Sized + 'a> fmt::Display for FmtHeader<'a, H> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt_header(f)
}
}
struct ValueString<'a>(&'a Item); struct ValueString<'a>(&'a Item);
impl<'a> fmt::Debug for ValueString<'a> { impl<'a> fmt::Debug for ValueString<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(f.write_str("\"")); try!(f.write_str("\""));
try!(self.0.write_h1(&mut MultilineFormatter(Multi::Join(true, f)))); try!(self.0.write_h1(&mut Formatter(Multi::Join(true, f))));
f.write_str("\"") f.write_str("\"")
} }
} }
impl<'a> fmt::Display for ValueString<'a> { impl<'a> fmt::Display for ValueString<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.write_h1(&mut MultilineFormatter(Multi::Join(true, f))) self.0.write_h1(&mut Formatter(Multi::Join(true, f)))
} }
} }
@@ -229,7 +248,7 @@ impl<'a, H: Header> fmt::Debug for HeaderValueString<'a, H> {
#[inline] #[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(f.write_str("\"")); try!(f.write_str("\""));
try!(self.0.fmt_multi_header(&mut MultilineFormatter(Multi::Join(true, f)))); try!(self.0.fmt_header(&mut Formatter(Multi::Join(true, f))));
f.write_str("\"") f.write_str("\"")
} }
} }
@@ -254,17 +273,6 @@ impl<'a, F: fmt::Write + 'a> fmt::Write for NewlineReplacer<'a, F> {
} }
} }
#[doc(hidden)]
pub trait HeaderClone {
fn clone_box(&self) -> Box<Header + Send + Sync>;
}
impl<T: Header + Clone> HeaderClone for T {
#[inline]
fn clone_box(&self) -> Box<Header + Send + Sync> {
Box::new(self.clone())
}
}
impl Header + Send + Sync { impl Header + Send + Sync {
// A trait object looks like this: // A trait object looks like this:
@@ -606,7 +614,7 @@ impl<'a> HeaderView<'a> {
impl<'a> fmt::Display for HeaderView<'a> { impl<'a> fmt::Display for HeaderView<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.1.write_h1(&mut MultilineFormatter(Multi::Line(self.0.as_ref(), f))) self.1.write_h1(&mut Formatter(Multi::Line(self.0.as_ref(), f)))
} }
} }
@@ -746,7 +754,13 @@ mod tests {
} }
} }
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt_header(&self, f: &mut super::Formatter) -> fmt::Result {
f.fmt_line(self)
}
}
impl fmt::Display for CrazyLength {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let CrazyLength(ref opt, ref value) = *self; let CrazyLength(ref opt, ref value) = *self;
write!(f, "{:?}, {:?}", opt, value) write!(f, "{:?}, {:?}", opt, value)
} }