mirror of
https://github.com/ntex-rs/ntex.git
synced 2025-04-06 22:37:39 +03:00
impl FromIterator for HeaderMap (#118)
* impl FromIterator for HeaderMap * impl TryFrom<&str> for Value
This commit is contained in:
parent
8473cf3c55
commit
d732657941
2 changed files with 146 additions and 1 deletions
|
@ -2,4 +2,8 @@
|
||||||
|
|
||||||
## [0.1.0] - 2022-06-20
|
## [0.1.0] - 2022-06-20
|
||||||
|
|
||||||
|
* impl TryFrom<&str> for Value
|
||||||
|
|
||||||
|
* impl FromIterator for HeaderMap
|
||||||
|
|
||||||
* Move http types to separate crate
|
* Move http types to separate crate
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use core::iter::FromIterator;
|
||||||
use std::collections::{self, hash_map, hash_map::Entry};
|
use std::collections::{self, hash_map, hash_map::Entry};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
|
@ -60,7 +61,56 @@ impl Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ValueIntoIter {
|
||||||
|
value: Value,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for ValueIntoIter {
|
||||||
|
type Item = HeaderValue;
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
match &mut self.value {
|
||||||
|
Value::One(_) => {
|
||||||
|
let val = std::mem::replace(&mut self.value, Value::Multi(Vec::new()));
|
||||||
|
match val {
|
||||||
|
Value::One(val) => Some(val),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Value::Multi(vec) => vec.pop(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
|
match self.value {
|
||||||
|
Value::One(_) => (1, None),
|
||||||
|
Value::Multi(ref v) => v.iter().size_hint(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoIterator for Value {
|
||||||
|
type Item = HeaderValue;
|
||||||
|
type IntoIter = ValueIntoIter;
|
||||||
|
#[inline]
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
ValueIntoIter { value: self }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Extend<HeaderValue> for Value {
|
||||||
|
#[inline]
|
||||||
|
fn extend<T>(&mut self, iter: T)
|
||||||
|
where
|
||||||
|
T: IntoIterator<Item = HeaderValue>,
|
||||||
|
{
|
||||||
|
let mut iter = iter.into_iter();
|
||||||
|
while let Some(h) = iter.next() {
|
||||||
|
self.append(h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for HeaderMap {
|
impl Default for HeaderMap {
|
||||||
|
#[inline]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new()
|
Self::new()
|
||||||
}
|
}
|
||||||
|
@ -307,6 +357,71 @@ impl<'a> AsName for &'a String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<N: std::fmt::Display, V> FromIterator<(N, V)> for HeaderMap
|
||||||
|
where
|
||||||
|
HeaderName: TryFrom<N>,
|
||||||
|
Value: TryFrom<V>,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn from_iter<T: IntoIterator<Item = (N, V)>>(iter: T) -> Self {
|
||||||
|
let map = iter
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|(n, v)| {
|
||||||
|
let name = format!("{}", n);
|
||||||
|
match (HeaderName::try_from(n), Value::try_from(v)) {
|
||||||
|
(Ok(n), Ok(v)) => Some((n, v)),
|
||||||
|
(Ok(n), Err(_)) => {
|
||||||
|
log::warn!("failed to parse `{}` header value", n);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
(Err(_), Ok(_)) => {
|
||||||
|
log::warn!("invalid HTTP header name: {}", name);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
(Err(_), Err(_)) => {
|
||||||
|
log::warn!("invalid HTTP header name `{}` and value", name);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.fold(HashMap::default(), |mut map: HashMap<_, Value>, (n, v)| {
|
||||||
|
match map.entry(n) {
|
||||||
|
Entry::Occupied(mut oc) => oc.get_mut().extend(v),
|
||||||
|
Entry::Vacant(va) => {
|
||||||
|
let _ = va.insert(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
map
|
||||||
|
});
|
||||||
|
HeaderMap { inner: map }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromIterator<HeaderValue> for Value {
|
||||||
|
fn from_iter<T: IntoIterator<Item = HeaderValue>>(iter: T) -> Self {
|
||||||
|
let mut iter = iter.into_iter();
|
||||||
|
let value = iter.next().map(|h| Value::One(h));
|
||||||
|
let mut value = match value {
|
||||||
|
Some(v) => v,
|
||||||
|
_ => Value::One(HeaderValue::from_static("")),
|
||||||
|
};
|
||||||
|
value.extend(iter);
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&str> for Value {
|
||||||
|
type Error = crate::header::InvalidHeaderValue;
|
||||||
|
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||||
|
Ok(value
|
||||||
|
.split(',')
|
||||||
|
.filter(|v| !v.is_empty())
|
||||||
|
.map(|v| v.trim())
|
||||||
|
.filter_map(|v| HeaderValue::from_str(v).ok())
|
||||||
|
.collect::<Value>())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct GetAll<'a> {
|
pub struct GetAll<'a> {
|
||||||
idx: usize,
|
idx: usize,
|
||||||
item: Option<&'a Value>,
|
item: Option<&'a Value>,
|
||||||
|
@ -378,7 +493,6 @@ impl<'a> Iter<'a> {
|
||||||
|
|
||||||
impl<'a> Iterator for Iter<'a> {
|
impl<'a> Iterator for Iter<'a> {
|
||||||
type Item = (&'a HeaderName, &'a HeaderValue);
|
type Item = (&'a HeaderName, &'a HeaderValue);
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn next(&mut self) -> Option<(&'a HeaderName, &'a HeaderValue)> {
|
fn next(&mut self) -> Option<(&'a HeaderName, &'a HeaderValue)> {
|
||||||
if let Some(ref mut item) = self.current {
|
if let Some(ref mut item) = self.current {
|
||||||
|
@ -410,6 +524,33 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::header::CONTENT_TYPE;
|
use crate::header::CONTENT_TYPE;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_from_iter() {
|
||||||
|
let vec = vec![
|
||||||
|
("Connection", "keep-alive"),
|
||||||
|
("Accept", "text/html"),
|
||||||
|
(
|
||||||
|
"Accept",
|
||||||
|
"*/*, application/xhtml+xml, application/xml;q=0.9, image/webp,",
|
||||||
|
),
|
||||||
|
];
|
||||||
|
let map = HeaderMap::from_iter(vec);
|
||||||
|
assert_eq!(
|
||||||
|
map.get("Connection"),
|
||||||
|
Some(&HeaderValue::from_static("keep-alive"))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
map.get_all("Accept").collect::<Vec<&HeaderValue>>(),
|
||||||
|
vec![
|
||||||
|
&HeaderValue::from_static("image/webp"),
|
||||||
|
&HeaderValue::from_static("text/html"),
|
||||||
|
&HeaderValue::from_static("application/xml;q=0.9"),
|
||||||
|
&HeaderValue::from_static("*/*"),
|
||||||
|
&HeaderValue::from_static("application/xhtml+xml"),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_basics() {
|
fn test_basics() {
|
||||||
let m = HeaderMap::default();
|
let m = HeaderMap::default();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue