cargo_rdme/
utils.rs

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