Skip to main content

vexy_vsvg_plugin_sdk/
lib.rs

1// this_file: crates/vexy-vsvg-plugin-sdk/src/lib.rs
2
3//! Plugin SDK for vexy-vsvg — everything you need to build an SVG optimization plugin.
4//!
5//! This crate ships 52 ready-to-use plugins and the machinery to write your own.
6//! Each plugin implements the [`Plugin`] trait and walks the SVG AST using
7//! the [`Visitor`] pattern. The SDK handles registration, parameter parsing,
8//! and CSS selector matching so you can focus on the optimization logic.
9//!
10//! ## What's inside
11//!
12//! - **52 built-in plugins** in the [`plugins`] module — ports of every SVGO plugin,
13//!   same names, same behavior.
14//! - **[`registry`]** — pre-populated registry of all plugins, ready to use.
15//! - **[`selector`]** — CSS selector engine for plugins that target elements by selector.
16//! - **[`css_matching`]** — matches CSS selectors against AST elements.
17//! - **[`PluginWithParams`]** — trait for plugins that accept JSON configuration.
18//!
19//! ## Write a plugin in 4 steps
20//!
21//! 1. Define a struct for your plugin state.
22//! 2. Implement [`Plugin`] — give it a name, description, and `apply()` method.
23//! 3. Implement [`Visitor`] — override the hooks that matter (usually `visit_element_enter`).
24//! 4. Call [`walk_document`] from `apply()` to kick off traversal.
25//!
26//! ## Example
27//!
28//! ```ignore
29//! use vexy_vsvg_plugin_sdk::{Plugin, Visitor, Document, Element, walk_document};
30//! use anyhow::Result;
31//!
32//! struct MyPlugin;
33//!
34//! impl Plugin for MyPlugin {
35//!     fn name(&self) -> &'static str { "myPlugin" }
36//!     fn description(&self) -> &'static str { "Does something cool" }
37//!     fn apply(&self, doc: &mut Document) -> Result<()> {
38//!         let mut visitor = MyVisitor;
39//!         walk_document(&mut visitor, doc)?;
40//!         Ok(())
41//!     }
42//! }
43//!
44//! struct MyVisitor;
45//! impl Visitor<'_> for MyVisitor {
46//!     fn visit_element_enter(&mut self, element: &mut Element) -> Result<(), vexy_vsvg::VexyError> {
47//!         // Modify element here
48//!         Ok(())
49//!     }
50//! }
51//! ```
52
53// Re-export core types needed for plugin development
54pub use vexy_vsvg::{
55    ast::{Document, Element, Node},
56    visitor::{walk_document, walk_element, walk_node, Visitor},
57    Config, Plugin, PluginConfig, VexyError,
58};
59
60pub mod css_matching;
61pub mod registry;
62// Note: enhanced_registry was just a wrapper around the registry, merged into registry
63pub mod selector;
64
65// Plugin implementations
66pub mod plugins;
67
68// Helper macros for tests
69#[cfg(test)]
70mod plugin_test_macros;
71
72#[cfg(test)]
73mod property_tests;
74
75/// Extended trait for plugins that accept JSON parameters from `svgo.config.json`.
76///
77/// Many SVGO plugins have tunable behavior — `convertColors` has `shorthex`,
78/// `cleanupNumericValues` has `floatPrecision`, etc. This trait standardizes
79/// how those parameters flow from JSON config into a typed Rust struct.
80///
81/// The associated `Config` type is deserialized from the `params` field in
82/// the plugin config entry. If omitted, `Config::default()` kicks in.
83///
84/// ```rust,ignore
85/// #[derive(Deserialize, Default)]
86/// struct MyConfig { precision: usize }
87///
88/// impl PluginWithParams for MyPlugin {
89///     type Config = MyConfig;
90///     fn with_config(config: MyConfig) -> Self { MyPlugin { precision: config.precision } }
91/// }
92/// ```
93pub trait PluginWithParams: Plugin + Default {
94    /// The config struct, deserialized from the JSON `params` object.
95    type Config: serde::de::DeserializeOwned + Default;
96
97    /// Deserialize JSON params into the typed config. Returns a clear error
98    /// if the JSON doesn't match the expected shape.
99    fn parse_params(params: &serde_json::Value) -> anyhow::Result<Self::Config> {
100        serde_json::from_value(params.clone())
101            .map_err(|e| anyhow::anyhow!("Invalid parameters: {}", e))
102    }
103
104    /// Construct the plugin with the given config. Called by the registry
105    /// after parsing parameters.
106    fn with_config(config: Self::Config) -> Self;
107
108    /// Alias for `parse_params` — kept for backward compatibility.
109    fn parse_config(params: &serde_json::Value) -> anyhow::Result<Self::Config> {
110        Self::parse_params(params)
111    }
112}