microcad_lang/syntax/
doc_block.rs1use microcad_lang_base::{Refer, SrcRef, SrcReferrer, TreeDisplay, TreeState};
7
8#[derive(Clone, Debug, Default)]
10pub struct DocBlock(pub Refer<Vec<String>>);
11
12impl DocBlock {
13 pub fn new_builtin(comment: &str) -> Self {
15 Self(Refer::none(
16 comment.lines().map(|s| format!("/// {s}")).collect(),
17 ))
18 }
19
20 pub fn is_empty(&self) -> bool {
22 self.0.is_empty()
23 }
24
25 pub fn merge(a: &DocBlock, b: &DocBlock) -> DocBlock {
27 match (a.is_empty(), b.is_empty()) {
28 (true, true) => Self::default(),
29 (true, false) => b.clone(),
30 (false, true) => a.clone(),
31 _ => {
32 let merged =
33 a.0.iter()
34 .chain([String::default()].iter()) .chain(b.0.iter())
36 .cloned()
37 .collect::<Vec<_>>();
38 Self(Refer::new(
39 merged,
40 SrcRef::merge(&a.src_ref(), &b.src_ref()),
41 ))
42 }
43 }
44 }
45
46 pub fn fetch_lines(&self) -> Vec<String> {
48 self.0
49 .iter()
50 .filter_map(|s| s.strip_prefix("/// ").or(s.strip_prefix("///")))
51 .map(|s| s.trim_end().to_string())
52 .collect::<Vec<_>>()
53 }
54}
55
56impl SrcReferrer for DocBlock {
57 fn src_ref(&self) -> SrcRef {
58 self.0.src_ref()
59 }
60}
61
62impl std::fmt::Display for DocBlock {
63 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64 write!(f, "{}", &self.0.value.join("\n"))
65 }
66}
67
68impl TreeDisplay for DocBlock {
69 fn tree_print(&self, f: &mut std::fmt::Formatter, depth: TreeState) -> std::fmt::Result {
70 writeln!(
71 f,
72 "{:depth$}DocBlock: '{}'",
73 "",
74 microcad_lang_base::shorten!(self.0.first().cloned().unwrap_or_default())
75 )
76 }
77}
78#[test]
79fn doc_block_merge() {
80 let doc_a = DocBlock(Refer::none(vec!["/// line 1".to_string()]));
81 let doc_b = DocBlock(Refer::none(vec!["/// line 2".to_string()]));
82 let empty = DocBlock::default();
83
84 assert!(DocBlock::merge(&empty, &empty).is_empty());
86
87 let merge_with_empty_left = DocBlock::merge(&empty, &doc_a);
88 assert_eq!(merge_with_empty_left.0.value, vec!["/// line 1"]);
89
90 let merge_with_empty_right = DocBlock::merge(&doc_a, &empty);
91 assert_eq!(merge_with_empty_right.0.value, vec!["/// line 1"]);
92
93 let merged = DocBlock::merge(&doc_a, &doc_b);
95
96 let expected = vec![
98 "/// line 1".to_string(),
99 "".to_string(),
100 "/// line 2".to_string(),
101 ];
102
103 assert_eq!(merged.0.value, expected);
104}
105
106#[test]
107fn doc_block_fetch_text() {
108 let doc1 = DocBlock(Refer::none(vec![
110 "/// Line one".to_string(),
111 "/// Line two ".to_string(), ]));
113 assert_eq!(doc1.fetch_lines().join("\n"), "Line one\nLine two");
114
115 let doc2 = DocBlock(Refer::none(vec![
117 "///Space".to_string(),
118 "///No space".to_string(),
119 ]));
120 assert_eq!(doc2.fetch_lines().join("\n"), "Space\nNo space");
121
122 let doc3 = DocBlock(Refer::none(vec![
124 "/// Valid".to_string(),
125 "Invalid line".to_string(),
126 "/// Also valid".to_string(),
127 ]));
128 assert_eq!(doc3.fetch_lines().join("\n"), "Valid\nAlso valid");
129
130 let doc_empty = DocBlock::default();
132 assert_eq!(doc_empty.fetch_lines().join("\n"), "");
133}