1#![allow(missing_docs)]
17
18use wasm_bindgen::prelude::*;
19
20use crate::{render_png as r_png, render_svg as r_svg, Options, Symbology};
21
22#[wasm_bindgen(start)]
25pub fn __bwipp_rs_init() {
26 console_error_panic_hook::set_once();
28}
29
30#[wasm_bindgen]
31extern "C" {
32 #[wasm_bindgen(typescript_type = "Record<string, string | number | boolean> | undefined")]
33 pub type JsOpts;
34}
35
36#[wasm_bindgen(js_name = renderSvg)]
38pub fn render_svg(symbology_id: &str, data: &str, opts: Option<JsOpts>) -> Result<String, JsError> {
39 let sym = resolve_symbology(symbology_id)?;
40 let options = options_from_js(opts)?;
41 r_svg(sym, data, &options).map_err(|e| JsError::new(&e.to_string()))
42}
43
44#[wasm_bindgen(js_name = renderPng)]
46pub fn render_png(
47 symbology_id: &str,
48 data: &str,
49 opts: Option<JsOpts>,
50) -> Result<Vec<u8>, JsError> {
51 let sym = resolve_symbology(symbology_id)?;
52 let options = options_from_js(opts)?;
53 r_png(sym, data, &options).map_err(|e| JsError::new(&e.to_string()))
54}
55
56#[wasm_bindgen(js_name = listSymbologies)]
58pub fn list_symbologies() -> Vec<js_sys::JsString> {
59 Symbology::all()
60 .iter()
61 .map(|s| js_sys::JsString::from(s.id()))
62 .collect()
63}
64
65#[wasm_bindgen(js_name = listSymbologyDetails)]
69pub fn list_symbology_details() -> Vec<js_sys::Array> {
70 Symbology::all()
71 .iter()
72 .map(|s| {
73 let arr = js_sys::Array::new();
74 arr.push(&js_sys::JsString::from(s.id()).into());
75 arr.push(&js_sys::JsString::from(s.display_name()).into());
76 arr.push(&js_sys::JsString::from(s.category()).into());
77 arr.push(&js_sys::JsString::from(s.default_data()).into());
78 arr
79 })
80 .collect()
81}
82
83#[wasm_bindgen(js_name = defaultData)]
87pub fn default_data(symbology_id: &str) -> String {
88 match Symbology::from_id(symbology_id) {
89 Some(s) => s.default_data().to_string(),
90 None => String::new(),
91 }
92}
93
94#[wasm_bindgen(js_name = defaultExtras)]
99pub fn default_extras(symbology_id: &str) -> js_sys::Object {
100 let obj = js_sys::Object::new();
101 if let Some(s) = Symbology::from_id(symbology_id) {
102 for (k, v) in s.default_extras() {
103 let _ = js_sys::Reflect::set(
104 &obj,
105 &js_sys::JsString::from(*k).into(),
106 &js_sys::JsString::from(*v).into(),
107 );
108 }
109 }
110 obj
111}
112
113fn resolve_symbology(id: &str) -> Result<Symbology, JsError> {
114 Symbology::from_id(id).ok_or_else(|| JsError::new(&format!("unknown symbology id: {id}")))
115}
116
117fn options_from_js(opts: Option<JsOpts>) -> Result<Options, JsError> {
118 let Some(opts) = opts else {
119 return Ok(Options::default());
120 };
121 let js_val: JsValue = opts.into();
122 if js_val.is_null() || js_val.is_undefined() {
123 return Ok(Options::default());
124 }
125 let obj: js_sys::Object = js_val
126 .dyn_into()
127 .map_err(|_| JsError::new("options must be a plain object"))?;
128
129 let mut out = Options::default();
130 let entries = js_sys::Object::entries(&obj);
131 for i in 0..entries.length() {
132 let pair = js_sys::Array::from(&entries.get(i));
133 let key = pair
134 .get(0)
135 .as_string()
136 .ok_or_else(|| JsError::new("options key must be a string"))?;
137 let val = pair.get(1);
138 apply_option(&mut out, &key, val);
139 }
140 Ok(out)
141}
142
143fn apply_option(out: &mut Options, key: &str, val: JsValue) {
144 match key {
145 "scale" => {
146 if let Some(n) = val.as_f64() {
147 out.scale = n as u32;
148 }
149 }
150 "bar_height" | "barHeight" => {
151 if let Some(n) = val.as_f64() {
152 out.bar_height = n as u32;
153 }
154 }
155 "quiet_zone" | "quietZone" => {
156 if let Some(n) = val.as_f64() {
157 out.quiet_zone = n as u32;
158 }
159 }
160 "include_text" | "includeText" | "includetext" => {
161 if let Some(b) = val.as_bool() {
162 out.include_text = b;
163 } else if let Some(s) = val.as_string() {
164 out.include_text = s == "true";
165 }
166 }
167 _ => {
168 let value = if let Some(s) = val.as_string() {
169 s
170 } else if let Some(b) = val.as_bool() {
171 if b {
172 "true".to_string()
173 } else {
174 "false".to_string()
175 }
176 } else if let Some(n) = val.as_f64() {
177 n.to_string()
178 } else {
179 return;
180 };
181 out.extras.push((key.to_string(), value));
182 }
183 }
184}