Skip to main content

c2e_wasm/
lib.rs

1/*
2 * This program is free software: you can redistribute it and/or modify it under the terms of
3 * the GNU General Public License as published by the Free Software Foundation, either version
4 * 3 of the License, or (at your option) any later version.
5 *
6 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
7 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
8 * See the GNU General Public License for more details.
9 *
10 * You should have received a copy of the GNU General Public License along with this program. If
11 * not, see <https://www.gnu.org/licenses/>.
12 */
13
14//! JS bindings for [`c2e`].
15
16use std::fmt::Write;
17
18use c2e::{ast::Declaration, chumsky::Parser};
19use fmt::HtmlFormatter;
20use wasm_bindgen::prelude::*;
21
22mod fmt;
23
24/// Explain the given C source code declaration.
25#[wasm_bindgen]
26pub fn explain(formatter: &HtmlFormatter, src: &str) -> Result<String, Vec<String>> {
27    c2e::parser::parser()
28        .parse(src)
29        .into_result()
30        .map(|decls| explain_declarations(formatter, &decls))
31        .map_err(|errs| errs.into_iter().map(|err| err.to_string()).collect())
32}
33
34fn explain_declarations(formatter: &HtmlFormatter, decls: &[Declaration<'_>]) -> String {
35    match decls {
36        [] => String::new(),
37        [decl] => explain_to_html(formatter, decl),
38        [decls @ .., last] => {
39            let mut s = String::new();
40            for decl in decls {
41                write!(&mut s, "{};\n\n", explain_to_html(formatter, decl)).unwrap();
42            }
43            write!(&mut s, "{};", explain_to_html(formatter, last)).unwrap();
44            s
45        }
46    }
47}
48
49fn explain_to_html(formatter: &HtmlFormatter, declaration: &Declaration<'_>) -> String {
50    c2e::explainer::explain_declaration(declaration).format_to_string(formatter)
51}
52
53#[cfg(test)]
54mod tests {
55    use crate::fmt::ClassMapping;
56
57    use super::*;
58
59    use pretty_assertions::assert_eq;
60
61    fn get_formatter() -> HtmlFormatter {
62        let mapping = ClassMapping {
63            qualifier: Some("q".to_string()),
64            primitive_type: Some("p".to_string()),
65            user_defined_type: Some("u".to_string()),
66            identifier: Some("i".to_string()),
67            number: Some("n".to_string()),
68            quasi_keyword: Some("qk".to_string()),
69        };
70        HtmlFormatter::new(mapping)
71    }
72
73    #[test]
74    fn explain_empty() {
75        let output = explain(&get_formatter(), "").unwrap();
76        assert_eq!(output, "");
77    }
78
79    #[test]
80    fn explain_success_single() {
81        let output = explain(&get_formatter(), "int main()").unwrap();
82        assert_eq!(
83            output,
84            r#"a <span class="qk">function</span> named <span class="i">main</span> that takes no parameters and returns an <span class="p">int</span>"#
85        );
86    }
87
88    #[test]
89    fn explain_success_multiple() {
90        let output = explain(&get_formatter(), "int main(); int foo(int a);").unwrap();
91        assert_eq!(
92            output,
93            r#"a <span class="qk">function</span> named <span class="i">main</span> that takes no parameters and returns an <span class="p">int</span>;
94
95a <span class="qk">function</span> named <span class="i">foo</span> that takes (an <span class="p">int</span> named <span class="i">a</span>) and returns an <span class="p">int</span>;"#
96        );
97    }
98
99    #[test]
100    fn explain_error() {
101        let output = explain(&get_formatter(), "int main(");
102        let errors = output.unwrap_err();
103        assert_eq!(errors.len(), 1);
104        assert!(errors[0].contains("expected"));
105    }
106}