typed_headers/
util.rs

1use http::header::{self, HeaderMap, HeaderValue};
2use std::error;
3use std::fmt::{self, Write};
4use std::str::FromStr;
5
6use crate::{Error, Header, HeaderMapExt, ToValues};
7
8#[inline]
9pub fn is_token(s: &str) -> bool {
10    if s.is_empty() {
11        return false;
12    }
13
14    s.as_bytes().iter().all(|b| match *b {
15        b'a'..=b'z'
16        | b'A'..=b'Z'
17        | b'0'..=b'9'
18        | b'!'
19        | b'#'
20        | b'$'
21        | b'%'
22        | b'&'
23        | b'\''
24        | b'*'
25        | b'+'
26        | b'-'
27        | b'.'
28        | b'^'
29        | b'_'
30        | b'`'
31        | b'|'
32        | b'~' => true,
33        _ => false,
34    })
35}
36
37pub fn parse_single_value<T>(
38    values: &mut header::ValueIter<HeaderValue>,
39) -> Result<Option<T>, Error>
40where
41    T: FromStr,
42    T::Err: Into<Box<dyn error::Error + Sync + Send>>,
43{
44    match values.next() {
45        Some(value) => {
46            let value = value
47                .to_str()
48                .map_err(|_| Error::invalid_value())?
49                .trim()
50                .parse()
51                .map_err(|_| Error::invalid_value())?;
52            Ok(Some(value))
53        }
54        None => Ok(None),
55    }
56}
57
58pub fn encode_single_value<T>(value: &T, values: &mut ToValues)
59where
60    T: fmt::Display,
61{
62    let value = value.to_string();
63    let value = HeaderValue::from_str(&value).expect("failed to encode header");
64    values.append(value);
65}
66
67pub fn parse_comma_delimited<T>(
68    values: &mut header::ValueIter<HeaderValue>,
69) -> Result<Option<Vec<T>>, Error>
70where
71    T: FromStr,
72    T::Err: Into<Box<dyn error::Error + Sync + Send>>,
73{
74    let mut out = vec![];
75    let mut empty = true;
76    for value in values {
77        empty = false;
78
79        let value = value.to_str().map_err(|_| Error::invalid_value())?;
80        for elem in value.split(',') {
81            let elem = elem.trim();
82            if elem.is_empty() {
83                continue;
84            }
85
86            let elem = elem.parse().map_err(|_| Error::invalid_value())?;
87            out.push(elem);
88        }
89    }
90
91    if empty {
92        Ok(None)
93    } else {
94        Ok(Some(out))
95    }
96}
97
98pub fn encode_comma_delimited<I>(elements: I, values: &mut ToValues)
99where
100    I: IntoIterator,
101    I::Item: fmt::Display,
102{
103    let mut out = String::new();
104    let mut it = elements.into_iter();
105    if let Some(elem) = it.next() {
106        write!(out, "{}", elem).unwrap();
107
108        for elem in it {
109            write!(out, ", {}", elem).unwrap();
110        }
111    }
112    let value = HeaderValue::from_str(&out).expect("failed to encode header");
113    values.append(value);
114}
115
116pub fn test_decode<H>(values: &[&str], expected: &H)
117where
118    H: Header + PartialEq + fmt::Debug,
119{
120    let mut map = HeaderMap::new();
121    for value in values {
122        let value = HeaderValue::from_str(value).unwrap();
123        map.append(H::name().clone(), value);
124    }
125
126    let header = map.typed_get::<H>().unwrap().unwrap();
127    assert_eq!(&header, expected);
128}
129
130pub fn test_encode<H>(header: &H, expected: &[&str])
131where
132    H: Header,
133{
134    let mut map = HeaderMap::new();
135    map.typed_insert(header);
136
137    let values = map.get_all(H::name()).iter().collect::<Vec<_>>();
138    assert_eq!(values.len(), expected.len());
139    for (value, expected) in values.iter().zip(expected) {
140        assert_eq!(value, expected);
141    }
142}
143
144pub fn test_round_trip<H>(header: &H, expected: &[&str])
145where
146    H: Header + PartialEq + fmt::Debug,
147{
148    let mut map = HeaderMap::new();
149    map.typed_insert(header);
150
151    let values = map.get_all(H::name()).iter().collect::<Vec<_>>();
152    assert_eq!(values.len(), expected.len());
153    for (value, expected) in values.iter().zip(expected) {
154        assert_eq!(value, expected);
155    }
156
157    let actual = map.typed_get::<H>().unwrap().unwrap();
158    assert_eq!(header, &actual);
159}