cargo_rdme/
utils.rs

1use itertools::Itertools;
2use std::ops::Range;
3
4#[derive(Eq, PartialEq, Debug, Clone, Ord, PartialOrd)]
5pub struct Span {
6    pub start: usize,
7    pub end: usize,
8}
9
10impl From<Range<usize>> for Span {
11    fn from(range: Range<usize>) -> Self {
12        Span { start: range.start, end: range.end }
13    }
14}
15
16#[derive(PartialEq, Eq, Debug)]
17pub enum ItemOrOther<'a, T> {
18    Item(T),
19    Other(&'a str),
20}
21
22pub struct MarkdownItemIterator<'a, T> {
23    source: &'a str,
24    iter: Box<dyn Iterator<Item = (Span, T)> + 'a>,
25}
26
27impl<'a, T> MarkdownItemIterator<'a, T> {
28    pub fn new(
29        source: &'a str,
30        iter: impl Iterator<Item = (Span, T)> + 'a,
31    ) -> MarkdownItemIterator<'a, T> {
32        MarkdownItemIterator { source, iter: Box::new(iter) }
33    }
34
35    #[allow(dead_code)]
36    pub fn items(self) -> impl Iterator<Item = T> + 'a
37    where
38        T: 'a,
39    {
40        self.iter.map(|(_, item)| item)
41    }
42
43    #[allow(dead_code)]
44    pub fn items_with_spans(self) -> impl Iterator<Item = (Span, T)> + 'a
45    where
46        T: 'a,
47    {
48        self.iter
49    }
50
51    pub fn complete(self) -> impl Iterator<Item = ItemOrOther<'a, T>>
52    where
53        T: Clone,
54    {
55        use std::iter::once;
56
57        once(None)
58            .chain(self.iter.map(Some))
59            .chain(once(None))
60            .tuple_windows()
61            .flat_map(|(l, r)| match (l, r) {
62                (None, Some((span, _))) => {
63                    [ItemOrOther::Other(&self.source[0..span.start]), ItemOrOther::Other("")]
64                }
65                (Some((span_1, v_1)), Some((span_2, _))) => [
66                    ItemOrOther::Item(v_1),
67                    ItemOrOther::Other(&self.source[span_1.end..span_2.start]),
68                ],
69                (Some((span, v)), None) => {
70                    [ItemOrOther::Item(v), ItemOrOther::Other(&self.source[span.end..])]
71                }
72                (None, None) => [ItemOrOther::Other(self.source), ItemOrOther::Other("")],
73            })
74            .filter(|e| !matches!(e, ItemOrOther::Other("")))
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81    use pretty_assertions::assert_eq;
82
83    #[test]
84    fn test_markdown_item_iterator_complete_no_item() {
85        let str = "hello world";
86
87        let mut iter = MarkdownItemIterator::<()>::new(str, std::iter::empty()).complete();
88
89        assert_eq!(iter.next(), Some(ItemOrOther::Other("hello world")));
90        assert_eq!(iter.next(), None);
91    }
92
93    #[test]
94    fn test_markdown_item_iterator_complete_item_start() {
95        let str = "_hello world";
96
97        let items = [(Span::from(0..1), "x")];
98
99        let mut iter = MarkdownItemIterator::new(str, items.into_iter()).complete();
100
101        assert_eq!(iter.next(), Some(ItemOrOther::Item("x")));
102        assert_eq!(iter.next(), Some(ItemOrOther::Other("hello world")));
103        assert_eq!(iter.next(), None);
104    }
105
106    #[test]
107    fn test_markdown_item_iterator_complete_item_end() {
108        let str = "hello world_";
109
110        let items = [(Span::from(11..12), "x")];
111
112        let mut iter = MarkdownItemIterator::new(str, items.into_iter()).complete();
113
114        assert_eq!(iter.next(), Some(ItemOrOther::Other("hello world")));
115        assert_eq!(iter.next(), Some(ItemOrOther::Item("x")));
116        assert_eq!(iter.next(), None);
117    }
118
119    #[test]
120    fn test_markdown_item_iterator_complete_item_middle() {
121        let str = "hello _ world";
122
123        let items = [(Span::from(6..7), "x")];
124
125        let mut iter = MarkdownItemIterator::new(str, items.into_iter()).complete();
126
127        assert_eq!(iter.next(), Some(ItemOrOther::Other("hello ")));
128        assert_eq!(iter.next(), Some(ItemOrOther::Item("x")));
129        assert_eq!(iter.next(), Some(ItemOrOther::Other(" world")));
130        assert_eq!(iter.next(), None);
131    }
132
133    #[test]
134    fn test_markdown_item_iterator_complete_item_multiple() {
135        let str = "hello _ world _ !";
136
137        let items = [(Span::from(6..7), "x"), (Span::from(14..15), "y")];
138
139        let mut iter = MarkdownItemIterator::new(str, items.into_iter()).complete();
140
141        assert_eq!(iter.next(), Some(ItemOrOther::Other("hello ")));
142        assert_eq!(iter.next(), Some(ItemOrOther::Item("x")));
143        assert_eq!(iter.next(), Some(ItemOrOther::Other(" world ")));
144        assert_eq!(iter.next(), Some(ItemOrOther::Item("y")));
145        assert_eq!(iter.next(), Some(ItemOrOther::Other(" !")));
146        assert_eq!(iter.next(), None);
147    }
148
149    #[test]
150    fn test_markdown_item_iterator_complete_consecutive() {
151        let str = "hello __ world!";
152
153        let items = [(Span::from(6..7), "x"), (Span::from(7..8), "y")];
154
155        let mut iter = MarkdownItemIterator::new(str, items.into_iter()).complete();
156
157        assert_eq!(iter.next(), Some(ItemOrOther::Other("hello ")));
158        assert_eq!(iter.next(), Some(ItemOrOther::Item("x")));
159        assert_eq!(iter.next(), Some(ItemOrOther::Item("y")));
160        assert_eq!(iter.next(), Some(ItemOrOther::Other(" world!")));
161        assert_eq!(iter.next(), None);
162    }
163}