add conversions of integers into HeaderValue (#218)

Implements `From<uN> for HeaderValue`, where `uN` is a bunch of integer types. This allows converting a number easily and efficiently into a `HeaderValue`.

It's especially nice when combined with the request/response builders:

```rust
let res = Response::builder()
    .header("content-length", my_str.len())
    .body(my_str)?;
```
This commit is contained in:
Sean McArthur
2018-06-22 10:54:21 -07:00
committed by GitHub
parent 64f76d8169
commit 071b49efa7
5 changed files with 122 additions and 2 deletions
+1
View File
@@ -24,6 +24,7 @@ categories = ["web-programming"]
[dependencies]
bytes = "0.4"
fnv = "1.0.5"
itoa = "0.4.1"
[dev-dependencies]
indexmap = "1.0"
+31
View File
@@ -111,3 +111,34 @@ impl From<header::InvalidHeaderValueBytes> for Error {
Error { inner: ErrorKind::HeaderValueShared(err) }
}
}
// A crate-private type until we can use !.
//
// Being crate-private, we should be able to swap the type out in a
// backwards compatible way.
pub enum Never {}
impl From<Never> for Error {
fn from(never: Never) -> Error {
match never {}
}
}
impl fmt::Debug for Never {
fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
match *self {}
}
}
impl fmt::Display for Never {
fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
match *self {}
}
}
impl error::Error for Never {
fn description(&self) -> &str {
match *self {}
}
}
+88 -2
View File
@@ -1,10 +1,11 @@
use bytes::Bytes;
use bytes::{Bytes, BytesMut};
use std::{cmp, fmt, str};
use std::{cmp, fmt, mem, str};
use std::error::Error;
use std::str::FromStr;
use ::convert::HttpTryFrom;
use ::error::Never;
use header::name::HeaderName;
/// Represents an HTTP header field value.
@@ -359,6 +360,91 @@ impl From<HeaderName> for HeaderValue {
}
}
macro_rules! from_integers {
($($name:ident: $t:ident => $max_len:expr),*) => {$(
impl From<$t> for HeaderValue {
fn from(num: $t) -> HeaderValue {
let mut buf = if mem::size_of::<BytesMut>() - 1 < $max_len {
// On 32bit platforms, BytesMut max inline size
// is 15 bytes, but the $max_len could be bigger.
//
// The likelihood of the number *actually* being
// that big is very small, so only allocate
// if the number needs that space.
//
// The largest decimal number in 15 digits:
// It wold be 10.pow(15) - 1, but this is a constant
// version.
if num as u64 > 999_999_999_999_999_999 {
BytesMut::with_capacity($max_len)
} else {
// fits inline...
BytesMut::new()
}
} else {
// full value fits inline, so don't allocate!
BytesMut::new()
};
let _ = ::itoa::fmt(&mut buf, num);
HeaderValue {
inner: buf.freeze(),
is_sensitive: false,
}
}
}
impl HttpTryFrom<$t> for HeaderValue {
type Error = Never;
#[inline]
fn try_from(num: $t) -> Result<Self, Self::Error> {
Ok(num.into())
}
}
#[test]
fn $name() {
let n: $t = 55;
let val = HeaderValue::from(n);
assert_eq!(val, &n.to_string());
let n = ::std::$t::MAX;
let val = HeaderValue::from(n);
assert_eq!(val, &n.to_string());
}
)*};
}
from_integers! {
// integer type => maximum decimal length
// u8 purposely left off... HeaderValue::from(b'3') could be confusing
from_u16: u16 => 5,
from_i16: i16 => 6,
from_u32: u32 => 10,
from_i32: i32 => 11,
from_u64: u64 => 20,
from_i64: i64 => 20
}
#[cfg(target_pointer_width = "16")]
from_integers! {
from_usize: usize => 5,
from_isize: isize => 6
}
#[cfg(target_pointer_width = "32")]
from_integers! {
from_usize: usize => 10,
from_isize: isize => 11
}
#[cfg(target_pointer_width = "64")]
from_integers! {
from_usize: usize => 20,
from_isize: isize => 20
}
#[cfg(test)]
mod from_header_name_tests {
use super::*;
+1
View File
@@ -161,6 +161,7 @@
extern crate bytes;
extern crate fnv;
extern crate itoa;
pub mod header;
pub mod method;
+1
View File
@@ -608,6 +608,7 @@ impl Builder {
/// let response = Response::builder()
/// .header("Content-Type", "text/html")
/// .header("X-Custom-Foo", "bar")
/// .header("content-length", 0)
/// .body(())
/// .unwrap();
/// ```