1 use std::fmt;
2 use std::iter::FromIterator;
3 use std::marker::PhantomData;
4 
5 use bytes::{Bytes, BytesMut};
6 use ::HeaderValue;
7 use ::util::TryFromValues;
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
40             .value
41             .to_str()
42             .ok()
43             .into_iter()
44             .flat_map(|value_str| {
45                 let mut in_quotes = false;
46                 value_str
47                     .split(move |c| {
48                         if in_quotes {
49                             if c == '"' {
50                                 in_quotes = false;
51                             }
52                             false // dont split
53                         } else {
54                             if c == Sep::CHAR {
55                                 true // split
56                             } else {
57                                 if c == '"' {
58                                     in_quotes = true;
59                                 }
60                                 false // dont split
61                             }
62                         }
63                     })
64                     .map(|item| item.trim())
65             })
66     }
67 }
68 
69 impl<Sep: Separator> TryFromValues for FlatCsv<Sep> {
try_from_values<'i, I>(values: &mut I) -> Result<Self, ::Error> where I: Iterator<Item = &'i HeaderValue>,70     fn try_from_values<'i, I>(values: &mut I) -> Result<Self, ::Error>
71     where
72         I: Iterator<Item = &'i HeaderValue>,
73     {
74         let flat = values.collect();
75         Ok(flat)
76     }
77 }
78 
79 impl<Sep> From<HeaderValue> for FlatCsv<Sep> {
from(value: HeaderValue) -> Self80     fn from(value: HeaderValue) -> Self {
81         FlatCsv {
82             value,
83             _marker: PhantomData,
84         }
85     }
86 }
87 
88 
89 impl<'a, Sep> From<&'a FlatCsv<Sep>> for HeaderValue {
from(flat: &'a FlatCsv<Sep>) -> HeaderValue90     fn from(flat: &'a FlatCsv<Sep>) -> HeaderValue {
91         flat.value.clone()
92     }
93 }
94 
95 impl<Sep> fmt::Debug for FlatCsv<Sep> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result96     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
97         fmt::Debug::fmt(&self.value, f)
98     }
99 }
100 
101 impl<'a, Sep: Separator> FromIterator<&'a HeaderValue> for FlatCsv<Sep> {
from_iter<I>(iter: I) -> Self where I: IntoIterator<Item = &'a HeaderValue>,102     fn from_iter<I>(iter: I) -> Self
103     where
104         I: IntoIterator<Item = &'a HeaderValue>,
105     {
106         let mut values = iter.into_iter();
107 
108         // Common case is there is only 1 value, optimize for that
109         if let (1, Some(1)) = values.size_hint() {
110             return values
111                 .next()
112                 .expect("size_hint claimed 1 item")
113                 .clone()
114                 .into();
115         }
116 
117         // Otherwise, there are multiple, so this should merge them into 1.
118         let bytes = values
119             .next()
120             .cloned()
121             .map(Bytes::from)
122             .unwrap_or_else(|| Bytes::new());
123 
124         let mut buf = BytesMut::from(bytes);
125 
126         for val in values {
127             buf.extend_from_slice(&[Sep::BYTE, b' ']);
128             buf.extend_from_slice(val.as_bytes());
129         }
130 
131         let val = HeaderValue::from_shared(buf.freeze())
132             .expect("comma separated HeaderValues are valid");
133 
134         val.into()
135     }
136 }
137 
138 // TODO: would be great if there was a way to de-dupe these with above
139 impl<Sep: Separator> FromIterator<HeaderValue> for FlatCsv<Sep> {
from_iter<I>(iter: I) -> Self where I: IntoIterator<Item = HeaderValue>,140     fn from_iter<I>(iter: I) -> Self
141     where
142         I: IntoIterator<Item = HeaderValue>,
143     {
144         let mut values = iter.into_iter();
145 
146         // Common case is there is only 1 value, optimize for that
147         if let (1, Some(1)) = values.size_hint() {
148             return values
149                 .next()
150                 .expect("size_hint claimed 1 item")
151                 .into();
152         }
153 
154         // Otherwise, there are multiple, so this should merge them into 1.
155         let bytes = values
156             .next()
157             .map(Bytes::from)
158             .unwrap_or_else(|| Bytes::new());
159 
160         let mut buf = BytesMut::from(bytes);
161 
162         for val in values {
163             buf.extend_from_slice(&[Sep::BYTE, b' ']);
164             buf.extend_from_slice(val.as_bytes());
165         }
166 
167         let val = HeaderValue::from_shared(buf.freeze())
168             .expect("comma separated HeaderValues are valid");
169 
170         val.into()
171     }
172 }
173 
174 #[cfg(test)]
175 mod tests {
176     use super::*;
177 
178     #[test]
comma()179     fn comma() {
180         let val = HeaderValue::from_static("aaa, b; bb, ccc");
181         let csv = FlatCsv::<Comma>::from(val);
182 
183         let mut values = csv.iter();
184         assert_eq!(values.next(), Some("aaa"));
185         assert_eq!(values.next(), Some("b; bb"));
186         assert_eq!(values.next(), Some("ccc"));
187         assert_eq!(values.next(), None);
188     }
189 
190     #[test]
semicolon()191     fn semicolon() {
192         let val = HeaderValue::from_static("aaa; b, bb; ccc");
193         let csv = FlatCsv::<SemiColon>::from(val);
194 
195         let mut values = csv.iter();
196         assert_eq!(values.next(), Some("aaa"));
197         assert_eq!(values.next(), Some("b, bb"));
198         assert_eq!(values.next(), Some("ccc"));
199         assert_eq!(values.next(), None);
200     }
201 
202     #[test]
quoted_text()203     fn quoted_text() {
204         let val = HeaderValue::from_static("foo=\"bar,baz\", sherlock=holmes");
205         let csv = FlatCsv::<Comma>::from(val);
206 
207         let mut values = csv.iter();
208         assert_eq!(values.next(), Some("foo=\"bar,baz\""));
209         assert_eq!(values.next(), Some("sherlock=holmes"));
210         assert_eq!(values.next(), None);
211     }
212 }
213