marked_text.rs

 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}