1use std::{collections::HashMap, ops::Range};
2
3pub fn marked_text_by(
4 marked_text: &str,
5 markers: Vec<char>,
6) -> (String, HashMap<char, Vec<usize>>) {
7 let mut extracted_markers: HashMap<char, Vec<usize>> = Default::default();
8 let mut unmarked_text = String::new();
9
10 for char in marked_text.chars() {
11 if markers.contains(&char) {
12 let char_offsets = extracted_markers.entry(char).or_insert(Vec::new());
13 char_offsets.push(unmarked_text.len());
14 } else {
15 unmarked_text.push(char);
16 }
17 }
18
19 (unmarked_text, extracted_markers)
20}
21
22pub fn marked_text(marked_text: &str) -> (String, Vec<usize>) {
23 let (unmarked_text, mut markers) = marked_text_by(marked_text, vec!['|']);
24 (unmarked_text, markers.remove(&'|').unwrap_or_else(Vec::new))
25}
26
27pub fn marked_text_ranges(marked_text: &str) -> (String, Vec<Range<usize>>) {
28 let (unmarked_text, mut markers) = marked_text_by(marked_text, vec!['[', ']']);
29 let opens = markers.remove(&'[').unwrap_or_default();
30 let closes = markers.remove(&']').unwrap_or_default();
31 assert_eq!(opens.len(), closes.len(), "marked ranges are unbalanced");
32
33 let ranges = opens
34 .into_iter()
35 .zip(closes)
36 .map(|(open, close)| {
37 assert!(close >= open, "marked ranges must be disjoint");
38 open..close
39 })
40 .collect();
41 (unmarked_text, ranges)
42}