Skip to main content

zpl_toolchain_jsonc_strip/
lib.rs

1//! Shared JSONC comment stripping utility.
2//!
3//! Supports:
4//! - `//` line comments
5//! - `/* ... */` block comments
6//! - string literal preservation (including escapes)
7
8/// Strip `//` and `/* */` comments from JSONC input.
9///
10/// Correctly handles escaped quotes inside strings and comment-like sequences
11/// embedded in string literals.
12#[must_use]
13pub fn strip_jsonc(input: &str) -> String {
14    let mut out = String::with_capacity(input.len());
15    let chars: Vec<char> = input.chars().collect();
16    let len = chars.len();
17    let mut i = 0usize;
18    let mut in_str = false;
19
20    while i < len {
21        let c = chars[i];
22
23        if in_str {
24            out.push(c);
25            if c == '\\' && i + 1 < len {
26                // Escaped character inside string: keep next char verbatim.
27                i += 1;
28                out.push(chars[i]);
29            } else if c == '"' {
30                in_str = false;
31            }
32            i += 1;
33            continue;
34        }
35
36        if c == '"' {
37            in_str = true;
38            out.push(c);
39            i += 1;
40            continue;
41        }
42
43        if c == '/' && i + 1 < len {
44            let c2 = chars[i + 1];
45            if c2 == '/' {
46                i += 2;
47                while i < len && chars[i] != '\n' {
48                    i += 1;
49                }
50                continue;
51            }
52            if c2 == '*' {
53                i += 2;
54                while i + 1 < len && !(chars[i] == '*' && chars[i + 1] == '/') {
55                    i += 1;
56                }
57                i = (i + 2).min(len);
58                continue;
59            }
60        }
61
62        out.push(c);
63        i += 1;
64    }
65    out
66}
67
68#[cfg(test)]
69mod tests {
70    use super::strip_jsonc;
71
72    #[test]
73    fn strips_line_and_block_comments() {
74        let input = r#"
75{
76  // comment
77  "a": 1, /* inline */ "b": 2
78}
79"#;
80        let stripped = strip_jsonc(input);
81        assert!(!stripped.contains("comment"));
82        assert!(!stripped.contains("inline"));
83        assert!(stripped.contains("\"a\": 1"));
84        assert!(stripped.contains("\"b\": 2"));
85    }
86
87    #[test]
88    fn preserves_comment_like_text_in_strings() {
89        let input = r#"{ "url": "http://example.com/*x*/", "note":"//keep" }"#;
90        let stripped = strip_jsonc(input);
91        assert!(stripped.contains("http://example.com/*x*/"));
92        assert!(stripped.contains("\"note\":\"//keep\""));
93    }
94}