1use 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}