website_playground/
lib.rs

1mod utils;
2
3use dfir_lang::diagnostic::{Diagnostic, Level};
4use dfir_lang::graph::{WriteConfig, build_hfcode};
5use proc_macro2::{LineColumn, Span};
6use quote::quote;
7use serde::{Deserialize, Serialize};
8use wasm_bindgen::prelude::*;
9
10#[wasm_bindgen]
11extern "C" {
12    fn alert(s: &str);
13    #[wasm_bindgen(js_namespace = console)]
14    fn log(s: &str);
15}
16
17#[wasm_bindgen]
18pub fn init() {
19    utils::set_panic_hook();
20}
21
22#[derive(Serialize, Deserialize)]
23pub struct JsLineColumn {
24    pub line: usize,
25    pub column: usize,
26}
27
28impl From<LineColumn> for JsLineColumn {
29    fn from(lc: LineColumn) -> Self {
30        JsLineColumn {
31            line: lc.line,
32            column: lc.column,
33        }
34    }
35}
36
37#[derive(Serialize, Deserialize)]
38pub struct JsSpan {
39    pub start: JsLineColumn,
40    pub end: Option<JsLineColumn>,
41}
42
43impl From<Span> for JsSpan {
44    fn from(span: Span) -> Self {
45        #[cfg(procmacro2_semver_exempt)]
46        let is_call_site = span.eq(&Span::call_site());
47
48        #[cfg(not(procmacro2_semver_exempt))]
49        let is_call_site = true;
50
51        if is_call_site {
52            JsSpan {
53                start: JsLineColumn { line: 0, column: 0 },
54                end: None,
55            }
56        } else {
57            JsSpan {
58                start: span.start().into(),
59                end: Some(span.end().into()),
60            }
61        }
62    }
63}
64
65#[derive(Serialize, Deserialize)]
66pub struct JsDiagnostic {
67    pub span: JsSpan,
68    pub message: String,
69    pub is_error: bool,
70}
71
72impl From<Diagnostic> for JsDiagnostic {
73    fn from(diag: Diagnostic) -> Self {
74        JsDiagnostic {
75            span: diag.span.into(),
76            message: diag.message,
77            is_error: diag.level == Level::Error,
78        }
79    }
80}
81
82#[derive(Serialize, Deserialize)]
83pub struct DfirResult {
84    pub output: Option<DfirOutput>,
85    pub diagnostics: Vec<JsDiagnostic>,
86}
87#[derive(Serialize, Deserialize)]
88pub struct DfirOutput {
89    pub compiled: String,
90    pub mermaid: String,
91}
92
93#[wasm_bindgen]
94#[allow(
95    clippy::allow_attributes,
96    clippy::too_many_arguments,
97    reason = "Easier to expose `bool` to JS than a config struct. TODO(mingwei):"
98)]
99pub fn compile_dfir(
100    program: String,
101    no_subgraphs: bool,
102    no_varnames: bool,
103    no_pull_push: bool,
104    no_handoffs: bool,
105    no_references: bool,
106    no_loops: bool,
107    op_short_text: bool,
108) -> JsValue {
109    let write_config = WriteConfig {
110        no_subgraphs,
111        no_varnames,
112        no_pull_push,
113        no_handoffs,
114        no_references,
115        no_loops,
116        op_short_text,
117        op_text_no_imports: false,
118    };
119
120    let out = match syn::parse_str(&program) {
121        Ok(input) => {
122            let (graph_code_opt, diagnostics) = build_hfcode(input, &quote!(dfir_rs));
123            let output = graph_code_opt.map(|(graph, code)| {
124                let mermaid = graph.to_mermaid(&write_config);
125                let file = syn::parse_quote! {
126                    fn main() {
127                        let mut df = #code;
128                        df.run_available();
129                    }
130                };
131                let compiled = prettyplease::unparse(&file);
132                DfirOutput { mermaid, compiled }
133            });
134            DfirResult {
135                output,
136                diagnostics: diagnostics.into_iter().map(Into::into).collect(),
137            }
138        }
139        Err(errors) => DfirResult {
140            output: None,
141            diagnostics: errors
142                .into_iter()
143                .map(|e| JsDiagnostic {
144                    span: e.span().into(),
145                    message: e.to_string(),
146                    is_error: true,
147                })
148                .collect(),
149        },
150    };
151
152    serde_wasm_bindgen::to_value(&out).unwrap()
153}