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}