website_playground/
lib.rs1mod 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, "e!(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}