1#[cfg(feature = "size-optimization")]
31pub mod minimal;
32
33#[cfg(target_arch = "wasm32")]
34pub mod enhanced;
35
36#[cfg(target_arch = "wasm32")]
37pub mod wasm_impl {
38 use serde::{Deserialize, Serialize};
39 use vexy_vsvg_core::{optimize_with_config, Config};
40 use wasm_bindgen::prelude::*;
41
42 #[cfg(feature = "wasm")]
46 #[global_allocator]
47 static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
48
49 #[cfg(debug_assertions)]
54 #[wasm_bindgen(start)]
55 pub fn init() {
56 #[cfg(feature = "console_error_panic_hook")]
57 console_error_panic_hook::set_once();
58 }
59
60 #[cfg(not(debug_assertions))]
64 #[wasm_bindgen(start)]
65 pub fn init() {
66 }
68
69 #[wasm_bindgen]
74 #[derive(Serialize, Deserialize)]
75 pub struct JsConfig {
76 path: Option<String>,
78 pub multipass: bool,
80 pub pretty: bool,
82 pub indent: u8,
84 plugins_json: String,
86 }
87
88 #[wasm_bindgen]
89 impl JsConfig {
90 #[wasm_bindgen(getter)]
91 pub fn path(&self) -> Option<String> {
92 self.path.clone()
93 }
94
95 #[wasm_bindgen(setter)]
96 pub fn set_path(&mut self, path: Option<String>) {
97 self.path = path;
98 }
99 }
100
101 #[wasm_bindgen]
102 impl JsConfig {
103 #[wasm_bindgen(constructor)]
105 pub fn new() -> Self {
106 Self {
107 path: None,
108 multipass: false,
109 pretty: false,
110 indent: 2,
111 plugins_json: "{}".to_string(),
112 }
113 }
114
115 #[wasm_bindgen(js_name = setPlugins)]
117 pub fn set_plugins(&mut self, plugins_json: &str) {
118 self.plugins_json = plugins_json.to_string();
119 }
120
121 #[wasm_bindgen(js_name = getPlugins)]
123 pub fn get_plugins(&self) -> String {
124 self.plugins_json.clone()
125 }
126 }
127
128 impl Default for JsConfig {
129 fn default() -> Self {
130 Self::new()
131 }
132 }
133
134 #[wasm_bindgen]
138 pub struct JsOptimizationResult {
139 data: String,
140 error: Option<String>,
141 pub original_size: usize,
142 pub optimized_size: usize,
143 }
144
145 #[wasm_bindgen]
146 impl JsOptimizationResult {
147 #[wasm_bindgen(getter)]
149 pub fn data(&self) -> String {
150 self.data.clone()
151 }
152
153 #[wasm_bindgen(getter)]
155 pub fn error(&self) -> Option<String> {
156 self.error.clone()
157 }
158
159 #[wasm_bindgen(getter, js_name = originalSize)]
161 pub fn original_size(&self) -> usize {
162 self.original_size
163 }
164
165 #[wasm_bindgen(getter, js_name = optimizedSize)]
167 pub fn optimized_size(&self) -> usize {
168 self.optimized_size
169 }
170
171 #[wasm_bindgen(getter, js_name = compressionRatio)]
173 pub fn compression_ratio(&self) -> f64 {
174 if self.original_size == 0 {
175 1.0
176 } else {
177 self.optimized_size as f64 / self.original_size as f64
178 }
179 }
180
181 #[wasm_bindgen(getter, js_name = sizeReduction)]
183 pub fn size_reduction(&self) -> f64 {
184 (1.0 - self.compression_ratio()) * 100.0
185 }
186 }
187
188 #[wasm_bindgen]
200 pub fn optimize(svg: &str, config: Option<JsConfig>) -> Result<JsOptimizationResult, JsError> {
201 let config = config.unwrap_or_default();
202 let original_size = svg.len();
203
204 let mut native_config = Config::with_default_preset();
206 native_config.multipass = config.multipass;
207 native_config.js2svg.pretty = config.pretty;
208 native_config.js2svg.indent = config.indent.to_string();
209
210 if !config.plugins_json.is_empty() && config.plugins_json != "{}" {
212 match serde_json::from_str::<serde_json::Value>(&config.plugins_json) {
213 Ok(plugins) => {
214 if let Some(plugins_obj) = plugins.as_object() {
215 for (name, enabled) in plugins_obj {
216 if let Some(enabled) = enabled.as_bool() {
217 native_config.set_plugin_enabled(name, enabled);
218 }
219 }
220 }
221 }
222 Err(e) => {
223 return Ok(JsOptimizationResult {
224 data: svg.to_string(),
225 error: Some(format!("Invalid plugins configuration: {}", e)),
226 original_size,
227 optimized_size: original_size,
228 });
229 }
230 }
231 }
232
233 match optimize_with_config(svg, native_config) {
235 Ok(result) => Ok(JsOptimizationResult {
236 data: result.data.clone(),
237 error: None,
238 original_size,
239 optimized_size: result.data.len(),
240 }),
241 Err(e) => Ok(JsOptimizationResult {
242 data: svg.to_string(),
243 error: Some(e.to_string()),
244 original_size,
245 optimized_size: original_size,
246 }),
247 }
248 }
249
250 #[wasm_bindgen(js_name = optimizeDefault)]
254 pub fn optimize_default(svg: &str) -> Result<JsOptimizationResult, JsError> {
255 optimize(svg, None)
256 }
257
258 #[wasm_bindgen(js_name = getVersion)]
260 pub fn get_version() -> String {
261 env!("CARGO_PKG_VERSION").to_string()
262 }
263
264 #[wasm_bindgen(js_name = getPlugins)]
266 pub fn get_plugins() -> Result<String, JsError> {
267 let config = Config::with_default_preset();
268 let plugins: Vec<String> = config
269 .plugins
270 .iter()
271 .map(|p| p.name().to_string())
272 .collect();
273 serde_json::to_string(&plugins).map_err(|e| JsError::new(&e.to_string()))
274 }
275
276 #[wasm_bindgen(js_name = getDefaultPreset)]
278 pub fn get_default_preset() -> Result<String, JsError> {
279 let config = Config::with_default_preset();
280 serde_json::to_string(&config).map_err(|e| JsError::new(&e.to_string()))
281 }
282
283 #[wasm_bindgen(js_name = optimizeChunked)]
289 pub fn optimize_chunked(
290 svg: &str,
291 config: Option<JsConfig>,
292 chunk_size: usize,
293 ) -> Result<JsOptimizationResult, JsError> {
294 let _ = chunk_size; optimize(svg, config)
298 }
299
300 #[wasm_bindgen(js_name = freeMemory)]
306 pub fn free_memory() {
307 }
310
311 #[cfg(test)]
312 mod tests {
313 use super::*;
314
315 #[test]
316 fn test_js_config_default() {
317 let config = JsConfig::new();
318 assert_eq!(config.multipass, false);
319 assert_eq!(config.pretty, false);
320 assert_eq!(config.indent, 2);
321 assert_eq!(config.plugins_json, "{}");
322 }
323
324 #[test]
325 fn test_js_config_plugins() {
326 let mut config = JsConfig::new();
327 config.set_plugins("{\"removeComments\": true}");
328 assert_eq!(config.get_plugins(), "{\"removeComments\": true}");
329 }
330
331 #[test]
332 fn test_optimization_result() {
333 let result = JsOptimizationResult {
334 data: "optimized".to_string(),
335 error: None,
336 original_size: 100,
337 optimized_size: 50,
338 };
339
340 assert_eq!(result.compression_ratio(), 0.5);
341 assert_eq!(result.size_reduction(), 50.0);
342 }
343 }
344}
345
346#[cfg(target_arch = "wasm32")]
348#[cfg(target_arch = "wasm32")]
349pub use enhanced::*;
350
351#[cfg(not(target_arch = "wasm32"))]
353pub mod stub {
354 }