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