1#![warn(missing_docs)]
4
5use js_sys::Array;
6use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue, UnwrapThrowExt};
7use web_sys::SvgsvgElement;
8
9#[wasm_bindgen]
10extern "C" {
11 #[wasm_bindgen (extends = js_sys::Object, js_name = Window)]
12 #[derive(Debug, Clone, PartialEq, Eq)]
13 type WindowWithViz;
14
15 #[wasm_bindgen (extends = js_sys::Object, js_name = Object)]
17 #[derive(Debug, Clone, PartialEq, Eq)]
18 pub type VizInstance;
19
20 #[wasm_bindgen(method, js_name = instance)]
21 async fn instance_raw(viz: &Viz) -> JsValue;
22}
23
24#[wasm_bindgen(getter_with_clone)]
26pub struct Options {
27 pub format: String,
29
30 pub engine: String,
32
33 #[wasm_bindgen(js_name = yInvert)]
35 pub y_invert: bool,
36}
37
38impl Default for Options {
39 fn default() -> Self {
40 Self {
41 format: "dot".to_string(),
42 engine: "dot".to_string(),
43 y_invert: false,
44 }
45 }
46}
47
48#[wasm_bindgen(module = "/js/viz-standalone.js")]
49extern "C" {
50 #[wasm_bindgen(catch)]
51 async fn graphviz_dummy_hack() -> Result<JsValue, JsValue>;
52
53 #[wasm_bindgen(js_namespace = window, js_name = Viz, getter, method)]
54 fn viz(this: &WindowWithViz) -> Viz;
55
56 #[wasm_bindgen (extends = js_sys::Object, js_name = Viz)]
57 #[derive(Debug, Clone, PartialEq, Eq)]
58 type Viz;
59
60 #[wasm_bindgen(js_name = graphvizVersion, getter, method)]
62 pub fn graphviz_version(viz_instance: &VizInstance) -> String;
63
64 #[wasm_bindgen(js_name = engines, getter, method)]
65 fn engines_raw(viz_instance: &VizInstance) -> Array;
66
67 #[wasm_bindgen(js_name = formats, getter, method)]
68 fn formats_raw(viz_instance: &VizInstance) -> Array;
69
70 #[wasm_bindgen(js_name = renderString, method, catch)]
71 fn render_string_raw(
72 viz_instance: &VizInstance,
73 src: String,
74 options: Options,
75 ) -> Result<JsValue, JsValue>;
76
77 #[wasm_bindgen(js_name = renderSVGElement, method, catch)]
78 fn render_svg_element_raw(
79 viz_instance: &VizInstance,
80 src: String,
81 options: Options,
82 ) -> Result<JsValue, JsValue>;
83
84 #[wasm_bindgen(js_name = renderJSON, method, catch)]
86 pub fn render_json(
87 viz_instance: &VizInstance,
88 src: String,
89 options: Options,
90 ) -> Result<JsValue, JsValue>;
91}
92
93impl Viz {
94 async fn instance(&self) -> VizInstance {
95 let _ = graphviz_dummy_hack().await;
99
100 Viz::instance_raw(self)
101 .await
102 .dyn_into::<VizInstance>()
103 .expect_throw("Could not intialize Graphviz")
104 }
105}
106
107impl VizInstance {
108 pub async fn new() -> VizInstance {
110 js_sys::global()
111 .dyn_into::<WindowWithViz>()
112 .expect_throw("Could not intialize Graphviz")
113 .viz()
114 .instance()
115 .await
116 }
117
118 pub fn engines(&self) -> Vec<String> {
120 VizInstance::engines_raw(self)
121 .into_iter()
122 .map(|js| js.as_string().expect_throw("Engine name is not a string"))
123 .collect()
124 }
125
126 pub fn formats(&self) -> Vec<String> {
128 VizInstance::formats_raw(self)
129 .into_iter()
130 .map(|js| js.as_string().expect_throw("Format is not a string"))
131 .collect()
132 }
133
134 pub fn render_string(&self, src: String, options: Options) -> Result<String, JsValue> {
136 VizInstance::render_string_raw(self, src, options).map(|res| {
137 res.as_string()
138 .expect_throw("Rendered object is not a string")
139 })
140 }
141
142 pub fn render_svg_element(
144 &self,
145 src: String,
146 options: Options,
147 ) -> Result<SvgsvgElement, JsValue> {
148 VizInstance::render_svg_element_raw(self, src, options).map(|res| {
149 res.try_into()
150 .expect_throw("Rendered object is not an svg element")
151 })
152 }
153}