substrate_data/forms/text/comments/
mod.rs1use super::{Spans, SyntaxError, parse};
8use crate::model::value::{Map, Value};
9use crate::schema::{COMMENT_KEY, META_PREFIX};
10
11pub fn with_comments(text: &str) -> Result<Value, SyntaxError> {
17 let doc = parse(text)?;
18 let mut value = doc.value;
19 let lead = comments(&text[..doc.spans.span.start as usize]);
20 let mut stray = Vec::new();
21 attach(&mut value, &doc.spans, text, &mut stray);
22 let mut notes: Vec<String> = lead.into_iter().collect();
23 notes.extend(stray);
24 if let (Value::Map(m), false) = (&mut value, notes.is_empty()) {
25 note(m, COMMENT_KEY, ¬es.join("\n"));
26 }
27 Ok(value)
28}
29
30fn attach(v: &mut Value, s: &Spans, text: &str, stray: &mut Vec<String>) {
33 let inner = s.span.start as usize + 1;
34 match v {
35 Value::Map(m) => {
36 let mut before = inner;
37 let mut entries: Vec<(String, Value)> = std::mem::take(m).into_iter().collect();
38 let mut out = Map::with_capacity(entries.len());
39 let mut own = Vec::new();
40 for ((key, child), cs) in entries.iter_mut().zip(&s.children) {
41 let start = cs.key.unwrap_or(cs.span).start as usize;
42 let said = comments(&text[before..start]);
43 before = cs.span.end as usize;
44 attach(child, cs, text, &mut own);
45 match (said, &mut *child) {
46 (Some(c), Value::Map(cm)) => note(cm, COMMENT_KEY, &c),
47 (Some(c), _) => {
48 out.insert(format!("{META_PREFIX}{key}"), Value::Str(c));
49 }
50 (None, _) => {}
51 }
52 out.insert(key.clone(), std::mem::take(child));
53 }
54 own.extend(comments(
55 &text[before..(s.span.end as usize).saturating_sub(1).max(before)],
56 ));
57 *m = out;
58 if !own.is_empty() {
59 note(m, COMMENT_KEY, &own.join("\n"));
60 }
61 }
62 Value::List(items) => {
63 let mut before = inner;
64 for (item, cs) in items.iter_mut().zip(&s.children) {
65 let said = comments(&text[before..cs.span.start as usize]);
66 before = cs.span.end as usize;
67 attach(item, cs, text, stray);
68 match (said, &mut *item) {
69 (Some(c), Value::Map(im)) => note(im, COMMENT_KEY, &c),
70 (Some(c), _) => stray.push(c),
71 (None, _) => {}
72 }
73 }
74 }
75 _ => {}
76 }
77}
78
79fn note(m: &mut Map, key: &str, text: &str) {
81 let joined = match m.get(key).and_then(Value::as_str) {
82 Some(had) => format!("{text}\n{had}"),
83 None => text.to_string(),
84 };
85 let rest: Vec<(String, Value)> = std::mem::take(m)
86 .into_iter()
87 .filter(|(k, _)| k != key)
88 .collect();
89 m.insert(key, Value::Str(joined));
90 for (k, v) in rest {
91 m.insert(k, v);
92 }
93}
94
95fn comments(gap: &str) -> Option<String> {
98 let mut lines = Vec::new();
99 let mut rest = gap;
100 while let Some(at) = rest.find('/') {
101 let after = &rest[at + 1..];
102 if let Some(line) = after.strip_prefix('/') {
103 let end = line.find('\n').unwrap_or(line.len());
104 lines.push(line[..end].trim().to_string());
105 rest = &line[end..];
106 } else if let Some(body) = after.strip_prefix('*') {
107 let end = body.find("*/").unwrap_or(body.len());
108 lines.extend(
109 body[..end]
110 .lines()
111 .map(|l| l.trim().trim_start_matches('*').trim().to_string())
112 .filter(|l| !l.is_empty()),
113 );
114 rest = body.get(end + 2..).unwrap_or_default();
115 } else {
116 rest = after;
117 }
118 }
119 (!lines.is_empty()).then(|| lines.join("\n"))
120}
121
122#[cfg(test)]
123mod tests;