diff --git a/src/buffer/buffer.rs b/src/buffer/buffer.rs index 8bb3d7c6..d89ca7dd 100644 --- a/src/buffer/buffer.rs +++ b/src/buffer/buffer.rs @@ -243,7 +243,7 @@ impl Buffer { pub fn set_line(&mut self, x: u16, y: u16, line: &Line<'_>, width: u16) -> (u16, u16) { let mut remaining_width = width; let mut x = x; - for span in &line.spans { + for span in line { if remaining_width == 0 { break; } diff --git a/src/text/line.rs b/src/text/line.rs index 08e68f42..e3ab935c 100644 --- a/src/text/line.rs +++ b/src/text/line.rs @@ -368,6 +368,43 @@ impl<'a> Line<'a> { pub fn reset_style(self) -> Self { self.patch_style(Style::reset()) } + + /// Returns an iterator over the spans of this line. + pub fn iter(&self) -> std::slice::Iter> { + self.spans.iter() + } + + /// Returns a mutable iterator over the spans of this line. + pub fn iter_mut(&mut self) -> std::slice::IterMut> { + self.spans.iter_mut() + } +} + +impl<'a> IntoIterator for Line<'a> { + type Item = Span<'a>; + type IntoIter = std::vec::IntoIter>; + + fn into_iter(self) -> Self::IntoIter { + self.spans.into_iter() + } +} + +impl<'a> IntoIterator for &'a Line<'a> { + type Item = &'a Span<'a>; + type IntoIter = std::slice::Iter<'a, Span<'a>>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl<'a> IntoIterator for &'a mut Line<'a> { + type Item = &'a mut Span<'a>; + type IntoIter = std::slice::IterMut<'a, Span<'a>>; + + fn into_iter(self) -> Self::IntoIter { + self.iter_mut() + } } impl<'a> From for Line<'a> { @@ -399,7 +436,7 @@ impl<'a> From> for Line<'a> { impl<'a> From> for String { fn from(line: Line<'a>) -> String { - line.spans.iter().fold(String::new(), |mut acc, s| { + line.iter().fold(String::new(), |mut acc, s| { acc.push_str(s.content.as_ref()); acc }) @@ -461,6 +498,8 @@ impl std::fmt::Display for Line<'_> { #[cfg(test)] mod tests { + use rstest::{fixture, rstest}; + use super::*; #[test] @@ -752,4 +791,92 @@ mod tests { let line = Line::from("Hello, world!").right_aligned(); assert_eq!(line.alignment, Some(Alignment::Right)); } + + mod iterators { + use super::*; + + /// a fixture used in the tests below to avoid repeating the same setup + #[fixture] + fn hello_world() -> Line<'static> { + Line::from(vec![ + Span::styled("Hello ", Color::Blue), + Span::styled("world!", Color::Green), + ]) + } + + #[rstest] + fn iter(hello_world: Line<'_>) { + let mut iter = hello_world.iter(); + assert_eq!(iter.next(), Some(&Span::styled("Hello ", Color::Blue))); + assert_eq!(iter.next(), Some(&Span::styled("world!", Color::Green))); + assert_eq!(iter.next(), None); + } + + #[rstest] + fn iter_mut(mut hello_world: Line<'_>) { + let mut iter = hello_world.iter_mut(); + assert_eq!(iter.next(), Some(&mut Span::styled("Hello ", Color::Blue))); + assert_eq!(iter.next(), Some(&mut Span::styled("world!", Color::Green))); + assert_eq!(iter.next(), None); + } + + #[rstest] + fn into_iter(hello_world: Line<'_>) { + let mut iter = hello_world.into_iter(); + assert_eq!(iter.next(), Some(Span::styled("Hello ", Color::Blue))); + assert_eq!(iter.next(), Some(Span::styled("world!", Color::Green))); + assert_eq!(iter.next(), None); + } + + #[rstest] + fn into_iter_ref(hello_world: Line<'_>) { + let mut iter = (&hello_world).into_iter(); + assert_eq!(iter.next(), Some(&Span::styled("Hello ", Color::Blue))); + assert_eq!(iter.next(), Some(&Span::styled("world!", Color::Green))); + assert_eq!(iter.next(), None); + } + + #[test] + fn into_iter_mut_ref() { + let mut hello_world = Line::from(vec![ + Span::styled("Hello ", Color::Blue), + Span::styled("world!", Color::Green), + ]); + let mut iter = (&mut hello_world).into_iter(); + assert_eq!(iter.next(), Some(&mut Span::styled("Hello ", Color::Blue))); + assert_eq!(iter.next(), Some(&mut Span::styled("world!", Color::Green))); + assert_eq!(iter.next(), None); + } + + #[rstest] + fn for_loop_ref(hello_world: Line<'_>) { + let mut result = String::new(); + for span in &hello_world { + result.push_str(span.content.as_ref()); + } + assert_eq!(result, "Hello world!"); + } + + #[rstest] + fn for_loop_mut_ref() { + let mut hello_world = Line::from(vec![ + Span::styled("Hello ", Color::Blue), + Span::styled("world!", Color::Green), + ]); + let mut result = String::new(); + for span in &mut hello_world { + result.push_str(span.content.as_ref()); + } + assert_eq!(result, "Hello world!"); + } + + #[rstest] + fn for_loop_into(hello_world: Line<'_>) { + let mut result = String::new(); + for span in hello_world { + result.push_str(span.content.as_ref()); + } + assert_eq!(result, "Hello world!"); + } + } } diff --git a/src/widgets/reflow.rs b/src/widgets/reflow.rs index 036f66b5..33bb81a9 100644 --- a/src/widgets/reflow.rs +++ b/src/widgets/reflow.rs @@ -357,8 +357,7 @@ mod test { let text = text.into(); let styled_lines = text.lines.iter().map(|line| { ( - line.spans - .iter() + line.iter() .flat_map(|span| span.styled_graphemes(Style::default())), line.alignment.unwrap_or(Alignment::Left), )