Skip to content

Commit 6b205d3

Browse files
tormolThomas Bahn
authored andcommitted
Implement DoubleEndedIterator for AsciiStr.lines()
1 parent 2725814 commit 6b205d3

File tree

1 file changed

+50
-11
lines changed

1 file changed

+50
-11
lines changed

src/ascii_str.rs

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -582,15 +582,10 @@ impl<'a> DoubleEndedIterator for Split<'a> {
582582
pub struct Lines<'a> {
583583
string: &'a AsciiStr,
584584
}
585-
586585
impl<'a> Iterator for Lines<'a> {
587586
type Item = &'a AsciiStr;
588587

589588
fn next(&mut self) -> Option<&'a AsciiStr> {
590-
if self.string.is_empty() {
591-
return None;
592-
}
593-
594589
if let Some(idx) = self.string
595590
.chars()
596591
.position(|&chr| chr == AsciiChar::LineFeed)
@@ -602,15 +597,34 @@ impl<'a> Iterator for Lines<'a> {
602597
};
603598
self.string = &self.string[idx + 1..];
604599
Some(line)
600+
} else if self.string.is_empty() {
601+
None
605602
} else {
606-
if !self.string.is_empty() {
607-
let line = self.string;
608-
self.string = &self.string[..0];
609-
Some(line)
610-
} else {
611-
None
603+
let line = self.string;
604+
self.string = &self.string[..0];
605+
Some(line)
606+
}
607+
}
608+
}
609+
impl<'a> DoubleEndedIterator for Lines<'a> {
610+
fn next_back(&mut self) -> Option<&'a AsciiStr> {
611+
if self.string.is_empty() {
612+
return None;
613+
}
614+
let mut i = self.string.len();
615+
if self.string[i-1] == AsciiChar::LineFeed {
616+
i -= 1;
617+
if i > 0 && self.string[i-1] == AsciiChar::CarriageReturn {
618+
i -= 1;
612619
}
613620
}
621+
self.string = &self.string[..i];
622+
while i > 0 && self.string[i-1] != AsciiChar::LineFeed {
623+
i -= 1;
624+
}
625+
let line = &self.string[i..];
626+
self.string = &self.string[..i];
627+
Some(line)
614628
}
615629
}
616630

@@ -946,13 +960,15 @@ mod tests {
946960
for (asciiline, line) in ascii.lines().zip(&lines) {
947961
assert_eq!(asciiline, *line);
948962
}
963+
assert_eq!(ascii.lines().count(), lines.len());
949964

950965
let lines: [&str; 4] = ["foo", "bar", "", "baz"];
951966
let joined = "foo\r\nbar\n\nbaz";
952967
let ascii = AsciiStr::from_ascii(joined.as_bytes()).unwrap();
953968
for (asciiline, line) in ascii.lines().zip(&lines) {
954969
assert_eq!(asciiline, *line);
955970
}
971+
assert_eq!(ascii.lines().count(), lines.len());
956972

957973
let trailing_line_break = b"\n";
958974
let ascii = AsciiStr::from_ascii(&trailing_line_break).unwrap();
@@ -970,6 +986,29 @@ mod tests {
970986
assert_eq!(4, iter_count);
971987
}
972988

989+
#[test]
990+
fn lines_iter_rev() {
991+
let joined = "foo\r\nbar\n\nbaz\n";
992+
let ascii = AsciiStr::from_ascii(joined.as_bytes()).unwrap();
993+
assert_eq!(ascii.lines().rev().count(), 4);
994+
assert_eq!(ascii.lines().rev().count(), joined.lines().rev().count());
995+
for (asciiline, line) in ascii.lines().rev().zip(joined.lines().rev()) {
996+
assert_eq!(asciiline, line);
997+
}
998+
let mut iter = ascii.lines();
999+
assert_eq!(iter.next(), Some("foo".as_ascii_str().unwrap()));
1000+
assert_eq!(iter.next_back(), Some("baz".as_ascii_str().unwrap()));
1001+
assert_eq!(iter.next_back(), Some("".as_ascii_str().unwrap()));
1002+
assert_eq!(iter.next(), Some("bar".as_ascii_str().unwrap()));
1003+
}
1004+
1005+
#[test]
1006+
fn lines_iter_empty() {
1007+
assert_eq!("".as_ascii_str().unwrap().lines().next(), None);
1008+
assert_eq!("".as_ascii_str().unwrap().lines().next_back(), None);
1009+
assert_eq!("".lines().next(), None);
1010+
}
1011+
9731012
#[test]
9741013
fn split_str() {
9751014
fn split_equals_str(haystack: &str, needle: char) {

0 commit comments

Comments
 (0)