1use std::fmt::Write;
17
18use c2e::{ast::Declaration, chumsky::Parser};
19use fmt::HtmlFormatter;
20use wasm_bindgen::prelude::*;
21
22mod fmt;
23
24#[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}