lines_inclusive/
lib.rs

1#![no_std]
2#![doc = include_str!("specs.md")]
3
4use core::{iter::FusedIterator, str::SplitInclusive};
5
6mod sealed {
7    pub trait Sealed {}
8    impl Sealed for str {}
9}
10
11/// Extension trait that provides `lines_inclusive` method for `str`.
12pub trait LinesInclusive: sealed::Sealed {
13    /// Split a string into multiple lines, every line may end with `\n`.
14    ///
15    /// Note that if a line ends with `\n\r`, the `\r` will be the first character of the next line.
16    fn lines_inclusive(&self) -> LinesInclusiveIter<'_>;
17}
18
19impl LinesInclusive for str {
20    fn lines_inclusive(&self) -> LinesInclusiveIter<'_> {
21        LinesInclusiveIter::new(self)
22    }
23}
24
25/// Iterator over inclusive lines of strings.
26///
27/// This struct is created by calling [`LinesInclusive::lines_inclusive`].
28#[derive(Debug, Clone)]
29pub struct LinesInclusiveIter<'a>(SplitInclusive<'a, char>);
30
31impl<'a> LinesInclusiveIter<'a> {
32    /// Like [`LinesInclusive::lines_inclusive`].
33    fn new(text: &'a str) -> Self {
34        LinesInclusiveIter(text.split_inclusive('\n'))
35    }
36}
37
38impl<'a> Iterator for LinesInclusiveIter<'a> {
39    type Item = &'a str;
40    fn next(&mut self) -> Option<Self::Item> {
41        self.0.next()
42    }
43}
44
45impl DoubleEndedIterator for LinesInclusiveIter<'_> {
46    fn next_back(&mut self) -> Option<Self::Item> {
47        self.0.next_back()
48    }
49}
50
51impl FusedIterator for LinesInclusiveIter<'_> {}
52
53#[doc = include_str!("README.md")]
54mod test_readme {}