From 9788fb4861abe563b31e1c73048682dab717d55a Mon Sep 17 00:00:00 2001 From: Noah Baldwin Date: Sun, 12 Jan 2025 12:18:01 +1100 Subject: [PATCH] feat(oxvg_optimiser): #39 remove unused ns --- crates/oxvg_optimiser/src/jobs/mod.rs | 1 + .../src/jobs/remove_unused_n_s.rs | 201 ++++++++++++++++++ ...emove_unused_n_s__remove_unused_n_s-2.snap | 9 + ...emove_unused_n_s__remove_unused_n_s-3.snap | 11 + ...emove_unused_n_s__remove_unused_n_s-4.snap | 11 + ...emove_unused_n_s__remove_unused_n_s-5.snap | 11 + ...emove_unused_n_s__remove_unused_n_s-6.snap | 11 + ...emove_unused_n_s__remove_unused_n_s-7.snap | 7 + ..._remove_unused_n_s__remove_unused_n_s.snap | 9 + 9 files changed, 271 insertions(+) create mode 100644 crates/oxvg_optimiser/src/jobs/remove_unused_n_s.rs create mode 100644 crates/oxvg_optimiser/src/jobs/snapshots/oxvg_optimiser__jobs__remove_unused_n_s__remove_unused_n_s-2.snap create mode 100644 crates/oxvg_optimiser/src/jobs/snapshots/oxvg_optimiser__jobs__remove_unused_n_s__remove_unused_n_s-3.snap create mode 100644 crates/oxvg_optimiser/src/jobs/snapshots/oxvg_optimiser__jobs__remove_unused_n_s__remove_unused_n_s-4.snap create mode 100644 crates/oxvg_optimiser/src/jobs/snapshots/oxvg_optimiser__jobs__remove_unused_n_s__remove_unused_n_s-5.snap create mode 100644 crates/oxvg_optimiser/src/jobs/snapshots/oxvg_optimiser__jobs__remove_unused_n_s__remove_unused_n_s-6.snap create mode 100644 crates/oxvg_optimiser/src/jobs/snapshots/oxvg_optimiser__jobs__remove_unused_n_s__remove_unused_n_s-7.snap create mode 100644 crates/oxvg_optimiser/src/jobs/snapshots/oxvg_optimiser__jobs__remove_unused_n_s__remove_unused_n_s.snap diff --git a/crates/oxvg_optimiser/src/jobs/mod.rs b/crates/oxvg_optimiser/src/jobs/mod.rs index 27c7585..24060f0 100644 --- a/crates/oxvg_optimiser/src/jobs/mod.rs +++ b/crates/oxvg_optimiser/src/jobs/mod.rs @@ -86,6 +86,7 @@ jobs! { convert_transform: ConvertTransform (is_default: true), remove_empty_attrs: RemoveEmptyAttrs (is_default: true), remove_empty_containers: RemoveEmptyContainers (is_default: true), + remove_unused_n_s: RemoveUnusedNS (is_default: true), merge_paths: MergePaths (is_default: true), sort_attrs: SortAttrs (is_default: true), sort_defs_children: SortDefsChildren (is_default: true), diff --git a/crates/oxvg_optimiser/src/jobs/remove_unused_n_s.rs b/crates/oxvg_optimiser/src/jobs/remove_unused_n_s.rs new file mode 100644 index 0000000..359b8da --- /dev/null +++ b/crates/oxvg_optimiser/src/jobs/remove_unused_n_s.rs @@ -0,0 +1,201 @@ +use std::collections::HashSet; + +use oxvg_ast::{ + attribute::{Attr, Attributes}, + element::Element, + visitor::{Context, PrepareOutcome, Visitor}, +}; +use serde::Deserialize; + +#[derive(Clone)] +pub struct RemoveUnusedNS { + enabled: bool, + unused_namespaces: HashSet, +} + +impl Visitor for RemoveUnusedNS { + type Error = String; + + fn prepare( + &mut self, + _document: &E, + _context_flags: &mut oxvg_ast::visitor::ContextFlags, + ) -> oxvg_ast::visitor::PrepareOutcome { + if self.enabled { + PrepareOutcome::none + } else { + PrepareOutcome::skip + } + } + + fn document(&mut self, document: &mut E) -> Result<(), Self::Error> { + document.for_each_element_child(|e| { + self.root_element(&e); + }); + Ok(()) + } + + fn exit_document( + &mut self, + document: &mut E, + _context: &Context, + ) -> Result<(), Self::Error> { + document.for_each_element_child(|e| { + self.exit_root_element(&e); + }); + Ok(()) + } +} + +impl RemoveUnusedNS { + fn root_element(&mut self, element: &E) { + if element.prefix().is_none() && element.local_name().as_ref() == "svg" { + for attr in element.attributes().iter() { + if attr.prefix().is_some_and(|p| p.as_ref() == "xmlns") { + self.unused_namespaces.insert(attr.local_name().to_string()); + } + } + } + if self.unused_namespaces.is_empty() { + return; + } + let Some(prefix) = element.prefix().as_ref().map(ToString::to_string) else { + return; + }; + + self.unused_namespaces.remove(&prefix); + for attr in element.attributes().iter() { + if let Some(prefix) = attr.prefix().as_ref().map(ToString::to_string) { + self.unused_namespaces.remove(&prefix); + } + } + } + + fn exit_root_element(&self, element: &E) { + if element.prefix().is_some() || element.local_name().as_ref() != "svg" { + return; + } + + for name in &self.unused_namespaces { + log::debug!("removing xmlns:{name}"); + element.remove_attribute(&format!("xmlns:{name}").into()); + } + } +} + +impl Default for RemoveUnusedNS { + fn default() -> Self { + Self { + enabled: true, + unused_namespaces: HashSet::new(), + } + } +} + +impl<'de> Deserialize<'de> for RemoveUnusedNS { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let enabled = bool::deserialize(deserializer)?; + Ok(Self { + enabled, + unused_namespaces: HashSet::new(), + }) + } +} + +#[test] +fn remove_unused_n_s() -> anyhow::Result<()> { + use crate::test_config; + + insta::assert_snapshot!(test_config( + r#"{ "removeUnusedNS": true }"#, + Some( + r#" + + test + +"# + ), + )?); + + // FIXME: rcdom removes used xmlns + insta::assert_snapshot!(test_config( + r#"{ "removeUnusedNS": true }"#, + Some( + r#" + + test + +"# + ), + )?); + + // FIXME: rcdom removes used xmlns + insta::assert_snapshot!(test_config( + r#"{ "removeUnusedNS": true }"#, + Some( + r#" + + + test + + +"# + ), + )?); + + // FIXME: rcdom removes used xmlns + insta::assert_snapshot!(test_config( + r#"{ "removeUnusedNS": true }"#, + Some( + r#" + + + test + + +"# + ), + )?); + + // FIXME: rcdom removes used xmlns + insta::assert_snapshot!(test_config( + r#"{ "removeUnusedNS": true }"#, + Some( + r#" + + + test + + +"# + ), + )?); + + // FIXME: rcdom removes used xmlns + insta::assert_snapshot!(test_config( + r#"{ "removeUnusedNS": true }"#, + Some( + r#" + + + test + + +"# + ), + )?); + + insta::assert_snapshot!(test_config( + r#"{ "removeUnusedNS": true }"#, + Some( + r#" + test +"# + ), + )?); + + Ok(()) +} diff --git a/crates/oxvg_optimiser/src/jobs/snapshots/oxvg_optimiser__jobs__remove_unused_n_s__remove_unused_n_s-2.snap b/crates/oxvg_optimiser/src/jobs/snapshots/oxvg_optimiser__jobs__remove_unused_n_s__remove_unused_n_s-2.snap new file mode 100644 index 0000000..de58e31 --- /dev/null +++ b/crates/oxvg_optimiser/src/jobs/snapshots/oxvg_optimiser__jobs__remove_unused_n_s__remove_unused_n_s-2.snap @@ -0,0 +1,9 @@ +--- +source: crates/oxvg_optimiser/src/jobs/remove_unused_n_s.rs +expression: "test_config(r#\"{ \"removeUnusedNS\": true }\"#,\nSome(r#\"\n \n test\n \n\"#),)?" +--- + + + test + + diff --git a/crates/oxvg_optimiser/src/jobs/snapshots/oxvg_optimiser__jobs__remove_unused_n_s__remove_unused_n_s-3.snap b/crates/oxvg_optimiser/src/jobs/snapshots/oxvg_optimiser__jobs__remove_unused_n_s__remove_unused_n_s-3.snap new file mode 100644 index 0000000..b5e0a8a --- /dev/null +++ b/crates/oxvg_optimiser/src/jobs/snapshots/oxvg_optimiser__jobs__remove_unused_n_s__remove_unused_n_s-3.snap @@ -0,0 +1,11 @@ +--- +source: crates/oxvg_optimiser/src/jobs/remove_unused_n_s.rs +expression: "test_config(r#\"{ \"removeUnusedNS\": true }\"#,\nSome(r#\"\n \n \n test\n \n \n\"#),)?" +--- + + + + test + + + diff --git a/crates/oxvg_optimiser/src/jobs/snapshots/oxvg_optimiser__jobs__remove_unused_n_s__remove_unused_n_s-4.snap b/crates/oxvg_optimiser/src/jobs/snapshots/oxvg_optimiser__jobs__remove_unused_n_s__remove_unused_n_s-4.snap new file mode 100644 index 0000000..66cd99d --- /dev/null +++ b/crates/oxvg_optimiser/src/jobs/snapshots/oxvg_optimiser__jobs__remove_unused_n_s__remove_unused_n_s-4.snap @@ -0,0 +1,11 @@ +--- +source: crates/oxvg_optimiser/src/jobs/remove_unused_n_s.rs +expression: "test_config(r#\"{ \"removeUnusedNS\": true }\"#,\nSome(r#\"\n \n \n test\n \n \n\"#),)?" +--- + + + + test + + + diff --git a/crates/oxvg_optimiser/src/jobs/snapshots/oxvg_optimiser__jobs__remove_unused_n_s__remove_unused_n_s-5.snap b/crates/oxvg_optimiser/src/jobs/snapshots/oxvg_optimiser__jobs__remove_unused_n_s__remove_unused_n_s-5.snap new file mode 100644 index 0000000..a6fcb19 --- /dev/null +++ b/crates/oxvg_optimiser/src/jobs/snapshots/oxvg_optimiser__jobs__remove_unused_n_s__remove_unused_n_s-5.snap @@ -0,0 +1,11 @@ +--- +source: crates/oxvg_optimiser/src/jobs/remove_unused_n_s.rs +expression: "test_config(r#\"{ \"removeUnusedNS\": true }\"#,\nSome(r#\"\n \n \n test\n \n \n\"#),)?" +--- + + + + test + + + diff --git a/crates/oxvg_optimiser/src/jobs/snapshots/oxvg_optimiser__jobs__remove_unused_n_s__remove_unused_n_s-6.snap b/crates/oxvg_optimiser/src/jobs/snapshots/oxvg_optimiser__jobs__remove_unused_n_s__remove_unused_n_s-6.snap new file mode 100644 index 0000000..a889a48 --- /dev/null +++ b/crates/oxvg_optimiser/src/jobs/snapshots/oxvg_optimiser__jobs__remove_unused_n_s__remove_unused_n_s-6.snap @@ -0,0 +1,11 @@ +--- +source: crates/oxvg_optimiser/src/jobs/remove_unused_n_s.rs +expression: "test_config(r#\"{ \"removeUnusedNS\": true }\"#,\nSome(r#\"\n \n \n test\n \n \n\"#),)?" +--- + + + + test + + + diff --git a/crates/oxvg_optimiser/src/jobs/snapshots/oxvg_optimiser__jobs__remove_unused_n_s__remove_unused_n_s-7.snap b/crates/oxvg_optimiser/src/jobs/snapshots/oxvg_optimiser__jobs__remove_unused_n_s__remove_unused_n_s-7.snap new file mode 100644 index 0000000..dfbbe8a --- /dev/null +++ b/crates/oxvg_optimiser/src/jobs/snapshots/oxvg_optimiser__jobs__remove_unused_n_s__remove_unused_n_s-7.snap @@ -0,0 +1,7 @@ +--- +source: crates/oxvg_optimiser/src/jobs/remove_unused_n_s.rs +expression: "test_config(r#\"{ \"removeUnusedNS\": true }\"#,\nSome(r#\"\n test\n\"#),)?" +--- + + test + diff --git a/crates/oxvg_optimiser/src/jobs/snapshots/oxvg_optimiser__jobs__remove_unused_n_s__remove_unused_n_s.snap b/crates/oxvg_optimiser/src/jobs/snapshots/oxvg_optimiser__jobs__remove_unused_n_s__remove_unused_n_s.snap new file mode 100644 index 0000000..d06a3fc --- /dev/null +++ b/crates/oxvg_optimiser/src/jobs/snapshots/oxvg_optimiser__jobs__remove_unused_n_s__remove_unused_n_s.snap @@ -0,0 +1,9 @@ +--- +source: crates/oxvg_optimiser/src/jobs/remove_unused_n_s.rs +expression: "test_config(r#\"{ \"removeUnusedNS\": true }\"#,\nSome(r#\"\n \n test\n \n\"#),)?" +--- + + + test + +