1 use std::fmt;
2 use std::iter::FromIterator;
3 use std::marker::PhantomData;
4 
5 use bytes::BytesMut;
6 use util::TryFromValues;
7 use HeaderValue;
8 
9 // A single `HeaderValue` that can flatten multiple values with commas.
10 #[derive(Clone, PartialEq, Eq, Hash)]
11 pub(crate) struct FlatCsv<Sep = Comma> {
12     pub(crate) value: HeaderValue,
13     _marker: PhantomData<Sep>,
14 }
15 
16 pub(crate) trait Separator {
17     const BYTE: u8;
18     const CHAR: char;
19 }
20 
21 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
22 pub(crate) enum Comma {}
23 
24 impl Separator for Comma {
25     const BYTE: u8 = b',';
26     const CHAR: char = ',';
27 }
28 
29 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
30 pub(crate) enum SemiColon {}
31 
32 impl Separator for SemiColon {
33     const BYTE: u8 = b';';
34     const CHAR: char = ';';
35 }
36 
37 impl<Sep: Separator> FlatCsv<Sep> {
iter(&self) -> impl Iterator<Item = &str>38     pub(crate) fn iter(&self) -> impl Iterator<Item = &str> {
39         self.value.to_str().ok().into_iter().flat_map(|value_str| {
40             let mut in_quotes = false;
41             value_str
42                 .split(move |c| {
43                     if in_quotes {
44                         if c == '"' {
45                             in_quotes = false;
46                         }
47                         false // dont split
48                     } else {
49                         if c == Sep::CHAR {
50                             true // split
51                         } else {
52                             if c == '"' {
53                                 in_quotes = true;
54                             }
55                             false // dont split
56                         }
57                     }
58                 })
59                 .map(|item| item.trim())
60         })
61     }
62 }
63 
64 impl<Sep: Separator> TryFromValues for FlatCsv<Sep> {
try_from_values<'i, I>(values: &mut I) -> Result<Self, ::Error> where I: Iterator<Item = &'i HeaderValue>,65     fn try_from_values<'i, I>(values: &mut I) -> Result<Self, ::Error>
66     where
67         I: Iterator<Item = &'i HeaderValue>,
68     {
69         let flat = values.collect();
70         Ok(flat)
71     }
72 }
73 
74 impl<Sep> From<HeaderValue> for FlatCsv<Sep> {
from(value: HeaderValue) -> Self75     fn from(value: HeaderValue) -> Self {
76         FlatCsv {
77             value,
78             _marker: PhantomData,
79         }
80     }
81 }
82 
83 impl<'a, Sep> From<&'a FlatCsv<Sep>> for HeaderValue {
from(flat: &'a FlatCsv<Sep>) -> HeaderValue84     fn from(flat: &'a FlatCsv<Sep>) -> HeaderValue {
85         flat.value.clone()
86     }
87 }
88 
89 impl<Sep> fmt::Debug for FlatCsv<Sep> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result90     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
91         fmt::Debug::fmt(&self.value, f)
92     }
93 }
94 
95 impl<'a, Sep: Separator> FromIterator<&'a HeaderValue> for FlatCsv<Sep> {
from_iter<I>(iter: I) -> Self where I: IntoIterator<Item = &'a HeaderValue>,96     fn from_iter<I>(iter: I) -> Self
97     where
98         I: IntoIterator<Item = &'a HeaderValue>,
99     {
100         let mut values = iter.into_iter();
101 
102         // Common case is there is only 1 value, optimize for that
103         if let (1, Some(1)) = values.size_hint() {
104             return values
105                 .next()
106                 .expect("size_hint claimed 1 item")
107                 .clone()
108                 .into();
109         }
110 
111         // Otherwise, there are multiple, so this should merge them into 1.
112         let mut buf = values
113             .next()
114             .cloned()
115             .map(|val| BytesMut::from(val.as_bytes()))
116             .unwrap_or_else(|| BytesMut::new());
117 
118         for val in values {
119             buf.extend_from_slice(&[Sep::BYTE, b' ']);
120             buf.extend_from_slice(val.as_bytes());
121         }
122 
123         let val =
124             HeaderValue::from_maybe_shared(buf.freeze()).expect("comma separated HeaderValues are valid");
125 
126         val.into()
127     }
128 }
129 
130 // TODO: would be great if there was a way to de-dupe these with above
131 impl<Sep: Separator> FromIterator<HeaderValue> for FlatCsv<Sep> {
from_iter<I>(iter: I) -> Self where I: IntoIterator<Item = HeaderValue>,132     fn from_iter<I>(iter: I) -> Self
133     where
134         I: IntoIterator<Item = HeaderValue>,
135     {
136         let mut values = iter.into_iter();
137 
138         // Common case is there is only 1 value, optimize for that
139         if let (1, Some(1)) = values.size_hint() {
140             return values.next().expect("size_hint claimed 1 item").into();
141         }
142 
143         // Otherwise, there are multiple, so this should merge them into 1.
144         let mut buf = values
145             .next()
146             .map(|val| BytesMut::from(val.as_bytes()))
147             .unwrap_or_else(|| BytesMut::new());
148 
149         for val in values {
150             buf.extend_from_slice(&[Sep::BYTE, b' ']);
151             buf.extend_from_slice(val.as_bytes());
152         }
153 
154         let val =
155             HeaderValue::from_maybe_shared(buf.freeze()).expect("comma separated HeaderValues are valid");
156 
157         val.into()
158     }
159 }
160 
161 #[cfg(test)]
162 mod tests {
163     use super::*;
164 
165     #[test]
comma()166     fn comma() {
167         let val = HeaderValue::from_static("aaa, b; bb, ccc");
168         let csv = FlatCsv::<Comma>::from(val);
169 
170         let mut values = csv.iter();
171         assert_eq!(values.next(), Some("aaa"));
172         assert_eq!(values.next(), Some("b; bb"));
173         assert_eq!(values.next(), Some("ccc"));
174         assert_eq!(values.next(), None);
175     }
176 
177     #[test]
semicolon()178     fn semicolon() {
179         let val = HeaderValue::from_static("aaa; b, bb; ccc");
180         let csv = FlatCsv::<SemiColon>::from(val);
181 
182         let mut values = csv.iter();
183         assert_eq!(values.next(), Some("aaa"));
184         assert_eq!(values.next(), Some("b, bb"));
185         assert_eq!(values.next(), Some("ccc"));
186         assert_eq!(values.next(), None);
187     }
188 
189     #[test]
quoted_text()190     fn quoted_text() {
191         let val = HeaderValue::from_static("foo=\"bar,baz\", sherlock=holmes");
192         let csv = FlatCsv::<Comma>::from(val);
193 
194         let mut values = csv.iter();
195         assert_eq!(values.next(), Some("foo=\"bar,baz\""));
196         assert_eq!(values.next(), Some("sherlock=holmes"));
197         assert_eq!(values.next(), None);
198     }
199 }
200