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