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