1use std::{
2 collections::HashMap,
3 ops::Range,
4 path::{Path, PathBuf},
5};
6use tempdir::TempDir;
7
8pub fn temp_tree(tree: serde_json::Value) -> TempDir {
9 let dir = TempDir::new("").unwrap();
10 write_tree(dir.path(), tree);
11 dir
12}
13
14fn write_tree(path: &Path, tree: serde_json::Value) {
15 use serde_json::Value;
16 use std::fs;
17
18 if let Value::Object(map) = tree {
19 for (name, contents) in map {
20 let mut path = PathBuf::from(path);
21 path.push(name);
22 match contents {
23 Value::Object(_) => {
24 fs::create_dir(&path).unwrap();
25 write_tree(&path, contents);
26 }
27 Value::Null => {
28 fs::create_dir(&path).unwrap();
29 }
30 Value::String(contents) => {
31 fs::write(&path, contents).unwrap();
32 }
33 _ => {
34 panic!("JSON object must contain only objects, strings, or null");
35 }
36 }
37 }
38 } else {
39 panic!("You must pass a JSON object to this helper")
40 }
41}
42
43pub fn sample_text(rows: usize, cols: usize, start_char: char) -> String {
44 let mut text = String::new();
45 for row in 0..rows {
46 let c: char = (start_char as u32 + row as u32) as u8 as char;
47 let mut line = c.to_string().repeat(cols);
48 if row < rows - 1 {
49 line.push('\n');
50 }
51 text += &line;
52 }
53 text
54}
55
56pub fn marked_text_by(
57 marked_text: &str,
58 markers: Vec<char>,
59) -> (String, HashMap<char, Vec<usize>>) {
60 let mut extracted_markers: HashMap<char, Vec<usize>> = Default::default();
61 let mut unmarked_text = String::new();
62
63 for char in marked_text.chars() {
64 if markers.contains(&char) {
65 let char_offsets = extracted_markers.entry(char).or_insert(Vec::new());
66 char_offsets.push(unmarked_text.len());
67 } else {
68 unmarked_text.push(char);
69 }
70 }
71
72 (unmarked_text, extracted_markers)
73}
74
75pub fn marked_text(marked_text: &str) -> (String, Vec<usize>) {
76 let (unmarked_text, mut markers) = marked_text_by(marked_text, vec!['|']);
77 (unmarked_text, markers.remove(&'|').unwrap_or_else(Vec::new))
78}
79
80pub fn marked_text_ranges(marked_text: &str) -> (String, Vec<Range<usize>>) {
81 let (unmarked_text, mut markers) = marked_text_by(marked_text, vec!['[', ']']);
82 let opens = markers.remove(&'[').unwrap_or_default();
83 let closes = markers.remove(&']').unwrap_or_default();
84 assert_eq!(opens.len(), closes.len(), "marked ranges are unbalanced");
85
86 let ranges = opens
87 .into_iter()
88 .zip(closes)
89 .map(|(open, close)| {
90 assert!(close >= open, "marked ranges must be disjoint");
91 open..close
92 })
93 .collect();
94 (unmarked_text, ranges)
95}