contlines.rs 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. use std::io;
  2. /// An iterator that abstracts over continuation characters on
  3. /// subsequent lines
  4. pub struct ContinuationLines<R: Iterator<Item = io::Result<String>>> {
  5. underlying: R,
  6. }
  7. impl<R: Iterator<Item = io::Result<String>>> ContinuationLines<R> {
  8. fn join_next(&mut self, mut past: String) -> Option<io::Result<String>> {
  9. let next = self.underlying.next();
  10. match next {
  11. None => Some(Ok(past)),
  12. Some(Err(err)) => Some(Err(err)),
  13. Some(Ok(ref new)) => {
  14. if new.ends_with("\\") {
  15. let end = new.len() - 1;
  16. past.push_str(&new[(0..end)]);
  17. self.join_next(past)
  18. } else {
  19. past.push_str(&new);
  20. Some(Ok(past))
  21. }
  22. }
  23. }
  24. }
  25. pub fn new(iter: R) -> ContinuationLines<R> {
  26. ContinuationLines { underlying: iter }
  27. }
  28. }
  29. impl<R: Iterator<Item = io::Result<String>>> Iterator for ContinuationLines<R> {
  30. type Item = io::Result<String>;
  31. fn next(&mut self) -> Option<io::Result<String>> {
  32. let next = self.underlying.next();
  33. match next {
  34. None => None,
  35. Some(Err(err)) => Some(Err(err)),
  36. Some(Ok(x)) => {
  37. if x.ends_with("\\") {
  38. let end = x.len() - 1;
  39. self.join_next(x[(0..end)].to_owned())
  40. } else {
  41. Some(Ok(x))
  42. }
  43. }
  44. }
  45. }
  46. }
  47. #[cfg(test)]
  48. mod tests {
  49. use super::ContinuationLines;
  50. use std::io::{BufRead, Cursor};
  51. fn test_contlines(input: &[u8], expected: Vec<&str>) {
  52. // build a ContinuationLines iterator from our input buffer,
  53. // and unwrap all the IO exceptions we would get
  54. let mut i = ContinuationLines::new(Cursor::new(input).lines()).map(Result::unwrap);
  55. // walk the expected values and make sure those are the ones
  56. // we're getting
  57. for e in expected.into_iter() {
  58. assert_eq!(i.next(), Some(e.to_owned()));
  59. }
  60. // and then make sure we're at the end
  61. assert_eq!(i.next(), None);
  62. }
  63. #[test]
  64. fn no_contlines() {
  65. test_contlines(b"foo\nbar\n", vec!["foo", "bar"]);
  66. }
  67. #[test]
  68. fn two_joined_lines() {
  69. test_contlines(b"foo\\\nbar\n", vec!["foobar"]);
  70. }
  71. #[test]
  72. fn three_joined_lines() {
  73. test_contlines(b"foo\\\nbar\\\nbaz\n", vec!["foobarbaz"]);
  74. }
  75. #[test]
  76. fn mixed_joins() {
  77. test_contlines(b"foo\nbar\\\nbaz\nquux\n", vec!["foo", "barbaz", "quux"]);
  78. }
  79. }