1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
#[cfg(test)] mod test; use std::str::Chars; pub struct QuotedParts<'a> { inner: Chars<'a>, extra: Option<Vec<String>> } impl<'a> QuotedParts<'a> { pub fn from(input: &'a str) -> Self { QuotedParts { inner: input.chars(), extra: None } } pub fn all(input: &'a str) -> Vec<String> { QuotedParts::from(input).collect() } fn parse_escapes(input: &str) -> String { let mut result = String::new(); let mut escaped = false; for c in input.chars() { if escaped { result.push(c); escaped = false; continue; } if c == '\\' { escaped = true; continue; } result.push(c); } result } fn extra(&mut self) -> Option<String> { let extra = self.extra.as_mut()?; if extra.len() == 0 { return None; } Some(extra.remove(0)) } } impl<'a> Iterator for QuotedParts<'a> { type Item = String; fn next(&mut self) -> Option<Self::Item> { if let Some(extra) = self.extra.as_mut() { if extra.is_empty() { return None; } return Some(QuotedParts::parse_escapes(&extra.remove(0))); } let mut current = String::new(); let mut inside = String::new(); let mut in_quote = false; for c in &mut self.inner { if in_quote { if c == '"' { return Some(QuotedParts::parse_escapes(&inside[1..])); } inside.push(c); continue; } if c == '"' { in_quote = true; if !current.is_empty() { inside += ¤t; current.clear(); } inside.push(c); continue; } if c == ' ' { if !current.is_empty() { return Some(QuotedParts::parse_escapes(¤t)); } continue; } current.push(c); } if !inside.is_empty() { self.extra = Some(inside.split(' ').filter(|x| !x.is_empty()).map(ToOwned::to_owned).collect()); } if !current.is_empty() { return Some(QuotedParts::parse_escapes(¤t)); } self.extra() } }