From d9d4f90686660e859833fe3eca591d89d20bc74b Mon Sep 17 00:00:00 2001 From: Alex Kazik Date: Sun, 8 Mar 2026 18:14:09 +0100 Subject: [PATCH 01/15] cargo fmt --- src/dom/element.rs | 4 ++-- src/dom/node.rs | 2 +- src/dom/span.rs | 2 +- tests/text.rs | 6 ++++-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/dom/element.rs b/src/dom/element.rs index 034cb10..b2ef576 100644 --- a/src/dom/element.rs +++ b/src/dom/element.rs @@ -47,7 +47,7 @@ pub struct Element { /// Span of the element in the parsed source #[serde(skip)] - pub source_span: SourceSpan + pub source_span: SourceSpan, } impl Default for Element { @@ -59,7 +59,7 @@ impl Default for Element { classes: vec![], attributes: HashMap::new(), children: vec![], - source_span: SourceSpan::default() + source_span: SourceSpan::default(), } } } diff --git a/src/dom/node.rs b/src/dom/node.rs index 905ee21..35266ab 100644 --- a/src/dom/node.rs +++ b/src/dom/node.rs @@ -131,4 +131,4 @@ mod tests { assert_eq!(node.element(), None); assert_eq!(node.comment(), Some("test")); } -} \ No newline at end of file +} diff --git a/src/dom/span.rs b/src/dom/span.rs index 070fc02..3b1f943 100644 --- a/src/dom/span.rs +++ b/src/dom/span.rs @@ -1,4 +1,4 @@ -use serde::{Serialize}; +use serde::Serialize; /// Span of the information in the parsed source. #[derive(Debug, Default, Clone, Serialize, PartialEq)] diff --git a/tests/text.rs b/tests/text.rs index 8776369..e1aeb60 100644 --- a/tests/text.rs +++ b/tests/text.rs @@ -49,11 +49,13 @@ fn it_can_parse_text_with_chevron() -> Result<()> { #[test] fn it_can_parse_text_in_paragraph_with_weird_formatting() -> Result<()> { - let html = indoc!(r" + let html = indoc!( + r"

This is a paragraph with some weird formatting.

- "); + " + ); let dom = Dom::parse(html)?; assert_json_snapshot!(dom); Ok(()) From afaaae34600cfbb26c512b57b54f8d6fde5a014d Mon Sep 17 00:00:00 2001 From: Alex Kazik Date: Sun, 8 Mar 2026 18:15:41 +0100 Subject: [PATCH 02/15] edition 2024, cargo update, cargo fmt --- Cargo.lock | 1591 +++++++++++++++++++++------------ Cargo.toml | 4 +- benches/bench_wikipedia.rs | 2 +- examples/get_all_href/main.rs | 2 +- src/dom/formatting.rs | 2 +- src/dom/mod.rs | 14 +- src/dom/node.rs | 4 +- src/lib.rs | 4 +- tests/node_iter.rs | 2 +- 9 files changed, 1048 insertions(+), 577 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f212495..d4dd04d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,15 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] [[package]] name = "anes" @@ -10,75 +19,82 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.3.2" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", - "is-terminal", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.0" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" -version = "0.2.0" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.0" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] name = "anstyle-wincon" -version = "1.0.1" +version = "3.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", - "windows-sys 0.48.0", + "once_cell_polyfill", + "windows-sys 0.61.2", ] +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + [[package]] name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi 0.1.19", + "hermit-abi", "libc", "winapi", ] [[package]] name = "autocfg" -version = "1.1.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "base64" -version = "0.21.0" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "bitflags" @@ -86,6 +102,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + [[package]] name = "block-buffer" version = "0.10.4" @@ -97,15 +119,15 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.12.2" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6ed94e98ecff0c12dd1b04c15ec0d7d9458ca8fe806cea6f12954efe74c63b" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "bytes" -version = "1.4.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "cast" @@ -115,21 +137,25 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.79" +version = "1.2.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" +dependencies = [ + "find-msvc-tools", + "shlex", +] [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "ciborium" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" dependencies = [ "ciborium-io", "ciborium-ll", @@ -138,15 +164,15 @@ dependencies = [ [[package]] name = "ciborium-io" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" [[package]] name = "ciborium-ll" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" dependencies = [ "ciborium-io", "half", @@ -158,41 +184,39 @@ version = "3.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ - "bitflags", + "bitflags 1.3.2", "clap_lex 0.2.4", - "indexmap", + "indexmap 1.9.3", "textwrap", ] [[package]] name = "clap" -version = "4.2.7" +version = "4.5.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34d21f9bf1b425d2968943631ec91202fe5e837264063503708b83013f8fc938" +checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a" dependencies = [ "clap_builder", "clap_derive", - "once_cell", ] [[package]] name = "clap_builder" -version = "4.2.7" +version = "4.5.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "914c8c79fb560f238ef6429439a30023c862f7a28e688c58f7203f12b29970bd" +checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876" dependencies = [ "anstream", "anstyle", - "bitflags", - "clap_lex 0.4.1", + "clap_lex 1.0.0", "strsim", ] [[package]] name = "clap_derive" -version = "4.2.0" +version = "4.5.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" +checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" dependencies = [ "heck", "proc-macro2", @@ -211,33 +235,43 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.4.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" +checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "console" -version = "0.15.5" +version = "0.15.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d79fbe8970a77e3e34151cc13d3b3e248aa0faaecb9f6091fa07ebefe5ad60" +checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" dependencies = [ "encode_unicode", - "lazy_static", "libc", - "windows-sys 0.42.0", + "once_cell", + "windows-sys 0.59.0", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", ] [[package]] name = "core-foundation" -version = "0.9.3" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" dependencies = [ "core-foundation-sys", "libc", @@ -245,15 +279,15 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.7" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] @@ -294,54 +328,42 @@ dependencies = [ "itertools", ] -[[package]] -name = "crossbeam-channel" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - [[package]] name = "crossbeam-deque" -version = "0.8.3" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ - "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" -version = "0.9.14" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ - "autocfg", - "cfg-if", "crossbeam-utils", - "memoffset", - "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.8.15" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" -dependencies = [ - "cfg-if", -] +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", "typenum", @@ -349,70 +371,79 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", ] +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "doc-comment" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +checksum = "780955b8b195a21ab8e4ac6b60dd1dbdcec1dc6c51c0617964b08c81785e12c9" [[package]] name = "either" -version = "1.8.1" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "encode_unicode" -version = "0.3.6" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" [[package]] name = "encoding_rs" -version = "0.8.32" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ "cfg-if", ] [[package]] -name = "errno" -version = "0.3.1" +name = "equivalent" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys 0.48.0", -] +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] -name = "errno-dragonfly" -version = "0.1.2" +name = "errno" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ - "cc", "libc", + "windows-sys 0.61.2", ] [[package]] name = "fastrand" -version = "1.9.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "fnv" @@ -420,6 +451,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "foreign-types" version = "0.3.2" @@ -437,58 +474,57 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" dependencies = [ "percent-encoding", ] [[package]] name = "futures-channel" -version = "0.3.28" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-io" -version = "0.3.28" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" [[package]] name = "futures-sink" -version = "0.3.28" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-core", "futures-io", "futures-task", "memchr", "pin-project-lite", - "pin-utils", "slab", ] @@ -502,11 +538,35 @@ dependencies = [ "version_check", ] +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", +] + [[package]] name = "h2" -version = "0.3.18" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" +checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" dependencies = [ "bytes", "fnv", @@ -514,7 +574,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 2.13.0", "slab", "tokio", "tokio-util", @@ -523,9 +583,14 @@ dependencies = [ [[package]] name = "half" -version = "1.8.2" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] [[package]] name = "hashbrown" @@ -534,40 +599,40 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] -name = "heck" -version = "0.4.1" +name = "hashbrown" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] [[package]] -name = "hermit-abi" -version = "0.1.19" +name = "hashbrown" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] -name = "hermit-abi" -version = "0.2.6" +name = "heck" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.3.1" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] [[package]] name = "html_parser" version = "0.7.0" dependencies = [ - "clap 4.2.7", + "clap 4.5.60", "criterion", "doc-comment", "indoc", @@ -584,9 +649,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.9" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes", "fnv", @@ -595,9 +660,9 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", "http", @@ -606,21 +671,21 @@ dependencies = [ [[package]] name = "httparse" -version = "1.8.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "httpdate" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "0.14.26" +version = "0.14.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" dependencies = [ "bytes", "futures-channel", @@ -633,7 +698,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2", + "socket2 0.5.10", "tokio", "tower-service", "tracing", @@ -642,10 +707,11 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.23.2" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ + "futures-util", "http", "hyper", "rustls", @@ -666,14 +732,112 @@ dependencies = [ "tokio-native-tls", ] +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + [[package]] name = "idna" -version = "0.3.0" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "icu_normalizer", + "icu_properties", ] [[package]] @@ -683,66 +847,54 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", ] [[package]] -name = "indoc" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f2cb48b81b1dc9f39676bf99f5499babfec7cd8fe14307f7b3d747208fb5690" - -[[package]] -name = "insta" -version = "1.29.0" +name = "indexmap" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a28d25139df397cbca21408bb742cf6837e04cdbebf1b07b760caf971d6a972" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ - "console", - "lazy_static", - "linked-hash-map", + "equivalent", + "hashbrown 0.16.1", "serde", - "similar", - "yaml-rust", + "serde_core", ] [[package]] -name = "instant" -version = "0.1.12" +name = "indoc" +version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" dependencies = [ - "cfg-if", + "rustversion", ] [[package]] -name = "io-lifetimes" -version = "1.0.10" +name = "insta" +version = "1.46.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +checksum = "e82db8c87c7f1ccecb34ce0c24399b8a73081427f3c7c50a5d597925356115e4" dependencies = [ - "hermit-abi 0.3.1", - "libc", - "windows-sys 0.48.0", + "console", + "once_cell", + "serde", + "similar", + "tempfile", ] [[package]] name = "ipnet" -version = "2.7.2" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" [[package]] -name = "is-terminal" -version = "0.4.7" +name = "is_terminal_polyfill" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" -dependencies = [ - "hermit-abi 0.3.1", - "io-lifetimes", - "rustix", - "windows-sys 0.48.0", -] +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "itertools" @@ -755,66 +907,61 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "js-sys" -version = "0.3.62" +version = "0.3.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68c16e1bfd491478ab155fd8b4896b86f9ede344949b641e61501e07c2b8b4d5" +checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" dependencies = [ + "once_cell", "wasm-bindgen", ] [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] -name = "libc" -version = "0.2.144" +name = "leb128fmt" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] -name = "linked-hash-map" -version = "0.5.6" +name = "libc" +version = "0.2.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" +checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" [[package]] name = "linux-raw-sys" -version = "0.3.7" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] -name = "log" -version = "0.4.17" +name = "litemap" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" [[package]] -name = "memchr" -version = "2.5.0" +name = "log" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] -name = "memoffset" -version = "0.8.0" +name = "memchr" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" -dependencies = [ - "autocfg", -] +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "mime" @@ -824,23 +971,21 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mio" -version = "0.8.6" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ "libc", - "log", "wasi", - "windows-sys 0.45.0", + "windows-sys 0.61.2", ] [[package]] name = "native-tls" -version = "0.2.11" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" dependencies = [ - "lazy_static", "libc", "log", "openssl", @@ -854,42 +999,38 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] -name = "num_cpus" -version = "1.15.0" +name = "once_cell" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" -dependencies = [ - "hermit-abi 0.2.6", - "libc", -] +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] -name = "once_cell" -version = "1.17.1" +name = "once_cell_polyfill" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "oorandom" -version = "11.1.3" +version = "11.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" [[package]] name = "openssl" -version = "0.10.52" +version = "0.10.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01b8574602df80f7b85fdfc5392fa884a4e3b3f4f35402c070ab34c3d3f78d56" +checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" dependencies = [ - "bitflags", + "bitflags 2.11.0", "cfg-if", "foreign-types", "libc", @@ -911,15 +1052,15 @@ dependencies = [ [[package]] name = "openssl-probe" -version = "0.1.5" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] name = "openssl-sys" -version = "0.9.87" +version = "0.9.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e17f59264b2809d77ae94f0e1ebabc434773f370d6ca667bd223ea10e06cc7e" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" dependencies = [ "cc", "libc", @@ -929,31 +1070,31 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.5.0" +version = "6.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" +checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.6.0" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e68e84bfb01f0507134eac1e9b410a12ba379d064eab48c50ba4ce329a527b70" +checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" dependencies = [ - "thiserror", + "memchr", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.6.0" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b79d4c71c865a25a4322296122e3924d30bc8ee0834c8bfc8b95f7f054afbfb" +checksum = "11f486f1ea21e6c10ed15d5a7c77165d0ee443402f0780849d1768e7d9d6fe77" dependencies = [ "pest", "pest_generator", @@ -961,9 +1102,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.6.0" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c435bf1076437b851ebc8edc3a18442796b30f1728ffea6262d59bbe28b077e" +checksum = "8040c4647b13b210a963c1ed407c1ff4fdfa01c31d6d2a098218702e6664f94f" dependencies = [ "pest", "pest_meta", @@ -974,38 +1115,31 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.6.0" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "745a452f8eb71e39ffd8ee32b3c5f51d03845f99786fa9b68db6ff509c505411" +checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220" dependencies = [ - "once_cell", "pest", "sha2", ] [[package]] name = "pin-project-lite" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" - -[[package]] -name = "pin-utils" -version = "0.1.0" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] name = "pkg-config" -version = "0.3.27" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "plotters" -version = "0.3.4" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" dependencies = [ "num-traits", "plotters-backend", @@ -1016,42 +1150,67 @@ dependencies = [ [[package]] name = "plotters-backend" -version = "0.3.4" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" [[package]] name = "plotters-svg" -version = "0.3.3" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" dependencies = [ "plotters-backend", ] [[package]] -name = "proc-macro2" -version = "1.0.56" +name = "potential_utf" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" dependencies = [ - "unicode-ident", + "zerovec", ] [[package]] -name = "quote" -version = "1.0.27" +name = "prettyplease" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", + "syn", ] [[package]] -name = "rayon" -version = "1.7.0" +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rayon" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" dependencies = [ "either", "rayon-core", @@ -1059,45 +1218,48 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.11.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" dependencies = [ - "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", - "num_cpus", ] [[package]] -name = "redox_syscall" -version = "0.3.5" +name = "regex" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ - "bitflags", + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", ] [[package]] -name = "regex" -version = "1.8.1" +name = "regex-automata" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ + "aho-corasick", + "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.7.1" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "reqwest" -version = "0.11.17" +version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13293b639a097af28fc8a90f22add145a9c954e49d77da06263d58cf44d5fb91" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ "base64", "bytes", @@ -1123,6 +1285,8 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", + "sync_wrapper", + "system-configuration", "tokio", "tokio-native-tls", "tokio-rustls", @@ -1137,59 +1301,73 @@ dependencies = [ [[package]] name = "ring" -version = "0.16.20" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", + "cfg-if", + "getrandom 0.2.17", "libc", - "once_cell", - "spin", "untrusted", - "web-sys", - "winapi", + "windows-sys 0.52.0", ] [[package]] name = "rustix" -version = "0.37.19" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags", + "bitflags 2.11.0", "errno", - "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] name = "rustls" -version = "0.20.8" +version = "0.21.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", "ring", + "rustls-webpki", "sct", - "webpki", ] [[package]] name = "rustls-pemfile" -version = "1.0.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ "base64", ] +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" [[package]] name = "same-file" @@ -1202,24 +1380,18 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.21" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ - "windows-sys 0.42.0", + "windows-sys 0.61.2", ] -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - [[package]] name = "sct" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ "ring", "untrusted", @@ -1227,12 +1399,12 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.8.2" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags", - "core-foundation", + "bitflags 2.11.0", + "core-foundation 0.10.1", "core-foundation-sys", "libc", "security-framework-sys", @@ -1240,28 +1412,44 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.8.0" +version = "2.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" dependencies = [ "core-foundation-sys", "libc", ] +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + [[package]] name = "serde" -version = "1.0.163" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.163" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -1270,13 +1458,15 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "itoa", - "ryu", + "memchr", "serde", + "serde_core", + "zmij", ] [[package]] @@ -1293,96 +1483,153 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.6" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", "digest", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "similar" -version = "2.2.1" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420acb44afdae038210c99e69aae24109f32f15500aa708e81d46c9f29d55fcf" +checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" [[package]] name = "slab" -version = "0.4.8" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" dependencies = [ - "autocfg", + "libc", + "windows-sys 0.52.0", ] [[package]] name = "socket2" -version = "0.4.9" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "winapi", + "windows-sys 0.61.2", ] [[package]] -name = "spin" -version = "0.5.2" +name = "stable_deref_trait" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "strsim" -version = "0.10.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.15" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tempfile" -version = "3.5.0" +version = "3.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +checksum = "82a72c767771b47409d2345987fda8628641887d5466101319899796367354a0" dependencies = [ - "cfg-if", "fastrand", - "redox_syscall", + "getrandom 0.4.2", + "once_cell", "rustix", - "windows-sys 0.45.0", + "windows-sys 0.61.2", ] [[package]] name = "textwrap" -version = "0.16.0" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" +checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" [[package]] name = "thiserror" -version = "1.0.40" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.40" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", @@ -1390,44 +1637,37 @@ dependencies = [ ] [[package]] -name = "tinytemplate" -version = "1.2.1" +name = "tinystr" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" dependencies = [ - "serde", - "serde_json", + "displaydoc", + "zerovec", ] [[package]] -name = "tinyvec" -version = "1.6.0" +name = "tinytemplate" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" dependencies = [ - "tinyvec_macros", + "serde", + "serde_json", ] -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - [[package]] name = "tokio" -version = "1.28.1" +version = "1.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aa32867d44e6f2ce3385e89dceb990188b8bb0fb25b0cf576647a6f98ac5105" +checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" dependencies = [ - "autocfg", "bytes", "libc", "mio", - "num_cpus", "pin-project-lite", - "socket2", - "windows-sys 0.48.0", + "socket2 0.6.3", + "windows-sys 0.61.2", ] [[package]] @@ -1442,116 +1682,111 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.23.4" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ "rustls", "tokio", - "webpki", ] [[package]] name = "tokio-util" -version = "0.7.8" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", - "tracing", ] [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ - "cfg-if", "pin-project-lite", "tracing-core", ] [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", ] [[package]] name = "try-lock" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typenum" -version = "1.16.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "ucd-trie" -version = "0.1.5" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" - -[[package]] -name = "unicode-bidi" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] -name = "unicode-normalization" -version = "0.1.22" +name = "unicode-xid" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "untrusted" -version = "0.7.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.3.1" +version = "2.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" dependencies = [ "form_urlencoded", "idna", "percent-encoding", + "serde", ] +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "vcpkg" @@ -1561,15 +1796,15 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "walkdir" -version = "2.3.3" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", @@ -1577,62 +1812,69 @@ dependencies = [ [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] -name = "wasm-bindgen" -version = "0.2.85" +name = "wasip2" +version = "1.0.2+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b6cb788c4e39112fbe1822277ef6fb3c55cd86b95cb3d3c4c1c9597e4ac74b4" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" dependencies = [ - "cfg-if", - "wasm-bindgen-macro", + "wit-bindgen", ] [[package]] -name = "wasm-bindgen-backend" -version = "0.2.85" +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e522ed4105a9d626d885b35d62501b30d9666283a5c8be12c14a8bdafe7822" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" dependencies = [ - "bumpalo", - "log", + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" +dependencies = [ + "cfg-if", "once_cell", - "proc-macro2", - "quote", - "syn", + "rustversion", + "wasm-bindgen-macro", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.35" +version = "0.4.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "083abe15c5d88556b77bdf7aef403625be9e327ad37c62c4e4129af740168163" +checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" dependencies = [ "cfg-if", + "futures-util", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.85" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "358a79a0cb89d21db8120cbfb91392335913e4890665b1a7981d9e956903b434" +checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1640,52 +1882,76 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.85" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4783ce29f09b9d93134d41297aded3a712b7b979e9c6f28c32cb88c973a94869" +checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" dependencies = [ + "bumpalo", "proc-macro2", "quote", "syn", - "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.85" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a901d592cafaa4d711bc324edfaff879ac700b19c3dfd60058d2b445be2691eb" +checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" +dependencies = [ + "unicode-ident", +] [[package]] -name = "web-sys" -version = "0.3.62" +name = "wasm-encoder" +version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b5f940c7edfdc6d12126d98c9ef4d1b3d470011c47c76a6581df47ad9ba721" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" dependencies = [ - "js-sys", - "wasm-bindgen", + "leb128fmt", + "wasmparser", ] [[package]] -name = "webpki" -version = "0.22.0" +name = "wasm-metadata" +version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" dependencies = [ - "ring", - "untrusted", + "anyhow", + "indexmap 2.13.0", + "wasm-encoder", + "wasmparser", ] [[package]] -name = "webpki-roots" -version = "0.22.6" +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags 2.11.0", + "hashbrown 0.15.5", + "indexmap 2.13.0", + "semver", +] + +[[package]] +name = "web-sys" +version = "0.3.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" +checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" dependencies = [ - "webpki", + "js-sys", + "wasm-bindgen", ] +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + [[package]] name = "winapi" version = "0.3.9" @@ -1704,11 +1970,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.5" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "winapi", + "windows-sys 0.61.2", ] [[package]] @@ -1717,167 +1983,372 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows-sys" -version = "0.42.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-targets 0.52.6", ] [[package]] name = "windows-sys" -version = "0.45.0" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets 0.42.2", + "windows-targets 0.52.6", ] [[package]] name = "windows-sys" -version = "0.48.0" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-targets 0.48.0", + "windows-link", ] [[package]] name = "windows-targets" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] name = "windows-targets" -version = "0.48.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.48.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.48.0" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.48.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.48.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.48.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winreg" -version = "0.10.1" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "winapi", + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap 2.13.0", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.11.0", + "indexmap 2.13.0", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap 2.13.0", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", ] [[package]] -name = "yaml-rust" -version = "0.4.5" +name = "zerocopy" +version = "0.8.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +checksum = "a789c6e490b576db9f7e6b6d661bcc9799f7c0ac8352f56ea20193b2681532e5" dependencies = [ - "linked-hash-map", + "zerocopy-derive", ] + +[[package]] +name = "zerocopy-derive" +version = "0.8.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f65c489a7071a749c849713807783f70672b28094011623e200cb86dcb835953" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml index aadb89b..84a8b3d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "html_parser" version = "0.7.0" authors = ["Mathias Iversen "] -edition = "2018" +edition = "2024" repository = "https://github.com/mathiversen/html-parser" license = "MIT" description = "A simple and general purpose html/xhtml parser" @@ -21,7 +21,7 @@ doc-comment = "0.3.3" [dev-dependencies] indoc = "2.0.1" -insta = { version = "1.29.0", features = ["json"]} +insta = { version = "1.29.0", features = ["json"] } tempfile = "3.5.0" criterion = "0.4.0" reqwest = { version = "0.11.16", features = ["blocking", "rustls-tls"] } diff --git a/benches/bench_wikipedia.rs b/benches/bench_wikipedia.rs index ce27491..13d9616 100644 --- a/benches/bench_wikipedia.rs +++ b/benches/bench_wikipedia.rs @@ -1,4 +1,4 @@ -use criterion::{criterion_group, criterion_main, Criterion}; +use criterion::{Criterion, criterion_group, criterion_main}; use html_parser::Dom; static HTML: &'static str = include_str!("./wikipedia-2020-12-21.html"); diff --git a/examples/get_all_href/main.rs b/examples/get_all_href/main.rs index b64058e..5c66064 100644 --- a/examples/get_all_href/main.rs +++ b/examples/get_all_href/main.rs @@ -8,7 +8,7 @@ fn main() -> Result<()> { let iter = dom.children.get(0).unwrap().into_iter(); let hrefs = iter.filter_map(|item| match item { - Node::Element(ref element) if element.name == "a" => element.attributes["href"].clone(), + Node::Element(element) if element.name == "a" => element.attributes["href"].clone(), _ => None, }); diff --git a/src/dom/formatting.rs b/src/dom/formatting.rs index 8851de1..2dfe125 100644 --- a/src/dom/formatting.rs +++ b/src/dom/formatting.rs @@ -1,6 +1,6 @@ -use crate::error::Error; use crate::Result; use crate::Rule; +use crate::error::Error; use pest::error::Error as PestError; /// This function abstracts the formatting of errors away from the core logic inside parser, diff --git a/src/dom/mod.rs b/src/dom/mod.rs index 8d7e4b2..63d83a8 100644 --- a/src/dom/mod.rs +++ b/src/dom/mod.rs @@ -1,11 +1,11 @@ use crate::Result; -use pest::{iterators::Pair, iterators::Pairs, Parser}; +use pest::{Parser, iterators::Pair, iterators::Pairs}; use serde::Serialize; use std::default::Default; +use crate::Rule; use crate::error::Error; use crate::grammar::Grammar; -use crate::Rule; pub mod element; pub mod formatting; @@ -198,7 +198,7 @@ impl Dom { for node in &dom.children { match node { // Nodes other than - reject and - Node::Element(ref el) if el.name.clone().to_lowercase() != "html" => { + Node::Element(el) if el.name.clone().to_lowercase() != "html" => { if el.name == "head" || el.name == "body" { return Err(Error::Parsing(format!( "A document fragment should not include {}", @@ -208,7 +208,7 @@ impl Dom { seen_elements = true; } // Nodes - one (before any other elements) is okay - Node::Element(ref el) if el.name.clone().to_lowercase() == "html" => { + Node::Element(el) if el.name.clone().to_lowercase() == "html" => { if seen_html || seen_elements { return Err(Error::Parsing(format!( "A document fragment should not include {}", @@ -314,7 +314,7 @@ impl Dom { return Err(Error::Parsing(format!( "Failed to create element at rule: {:?}", pair.as_rule() - ))) + ))); } } } @@ -348,7 +348,7 @@ impl Dom { return Err(Error::Parsing(format!( "Failed to parse attr value: {:?}", inner_pair.as_rule() - ))) + ))); } } } @@ -356,7 +356,7 @@ impl Dom { return Err(Error::Parsing(format!( "Failed to parse attr: {:?}", pair.as_rule() - ))) + ))); } } } diff --git a/src/dom/node.rs b/src/dom/node.rs index 35266ab..a9eda08 100644 --- a/src/dom/node.rs +++ b/src/dom/node.rs @@ -56,7 +56,7 @@ impl<'a> Iterator for NodeIntoIterator<'a> { fn next(&mut self) -> Option { // Get first child let child = match self.node { - Node::Element(ref e) => e.children.get(0), + Node::Element(e) => e.children.get(0), _ => None, }; @@ -76,7 +76,7 @@ impl<'a> Iterator for NodeIntoIterator<'a> { // Try to get the next sibling of the parent node if let Some((sibling_index, parent)) = self.index.pop() { let next_sibling = sibling_index + 1; - let sibling = if let Node::Element(ref e) = parent { + let sibling = if let Node::Element(e) = parent { e.children.get(next_sibling) } else { None diff --git a/src/lib.rs b/src/lib.rs index c7ef241..c91b753 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -102,9 +102,9 @@ mod grammar; use grammar::Rule; -pub use crate::dom::element::{Element, ElementVariant}; -pub use crate::dom::node::Node; pub use crate::dom::Dom; pub use crate::dom::DomVariant; +pub use crate::dom::element::{Element, ElementVariant}; +pub use crate::dom::node::Node; pub use crate::error::Error; pub use crate::error::Result; diff --git a/tests/node_iter.rs b/tests/node_iter.rs index 9892be1..2d1dfd8 100644 --- a/tests/node_iter.rs +++ b/tests/node_iter.rs @@ -20,7 +20,7 @@ fn it_can_iter_1() -> Result<()> { let dom = Dom::parse(&html)?; let root = dom.children.get(0).unwrap().into_iter(); let num_li = root.into_iter().fold(0, |mut acc, curr| match curr { - Node::Element(ref e) => { + Node::Element(e) => { if e.name == "li" { acc += 1; } From 194446aac27de492a6dfba897923705a34fb7d91 Mon Sep 17 00:00:00 2001 From: Alex Kazik Date: Sun, 8 Mar 2026 19:03:27 +0100 Subject: [PATCH 03/15] cargo clippy --- examples/get_all_href/main.rs | 2 +- src/dom/mod.rs | 17 ++++++----------- src/dom/node.rs | 10 ++++------ tests/node_iter.rs | 4 ++-- tests/svg.rs | 2 +- 5 files changed, 14 insertions(+), 21 deletions(-) diff --git a/examples/get_all_href/main.rs b/examples/get_all_href/main.rs index 5c66064..607427e 100644 --- a/examples/get_all_href/main.rs +++ b/examples/get_all_href/main.rs @@ -5,7 +5,7 @@ use html_parser::{Dom, Node, Result}; fn main() -> Result<()> { let html = include_str!("./index.html"); let dom = Dom::parse(html)?; - let iter = dom.children.get(0).unwrap().into_iter(); + let iter = dom.children.first().unwrap().into_iter(); let hrefs = iter.filter_map(|item| match item { Node::Element(element) if element.name == "a" => element.attributes["href"].clone(), diff --git a/src/dom/mod.rs b/src/dom/mod.rs index 63d83a8..15d738b 100644 --- a/src/dom/mod.rs +++ b/src/dom/mod.rs @@ -177,14 +177,13 @@ impl Dom { if dom .children .iter() - .filter(|x| match x { - Node::Element(el) if el.name.to_lowercase() == "html" => true, - _ => false, - }) + .filter(|x| matches!(x, Node::Element(el) if el.name.to_lowercase() == "html")) .count() > 1 { - return Err(Error::Parsing(format!("Document with multiple HTML tags",))); + return Err(Error::Parsing( + "Document with multiple HTML tags".to_string(), + )); } } @@ -318,7 +317,7 @@ impl Dom { } } } - if element.name != "" { + if !element.name.is_empty() { Ok(Some(Node::Element(element))) } else { Ok(None) @@ -336,11 +335,7 @@ impl Dom { attribute.1 = Some(pair.as_str().trim().to_string()); } Rule::attr_quoted => { - let inner_pair = pair - .into_inner() - .into_iter() - .next() - .expect("attribute value"); + let inner_pair = pair.into_inner().next().expect("attribute value"); match inner_pair.as_rule() { Rule::attr_value => attribute.1 = Some(inner_pair.as_str().to_string()), diff --git a/src/dom/node.rs b/src/dom/node.rs index a9eda08..ade46ae 100644 --- a/src/dom/node.rs +++ b/src/dom/node.rs @@ -56,11 +56,11 @@ impl<'a> Iterator for NodeIntoIterator<'a> { fn next(&mut self) -> Option { // Get first child let child = match self.node { - Node::Element(e) => e.children.get(0), + Node::Element(e) => e.children.first(), _ => None, }; - let result = match child { + match child { // If element has child, return child Some(child) => { self.index.push((0, self.node)); @@ -68,7 +68,7 @@ impl<'a> Iterator for NodeIntoIterator<'a> { Some(child) } // If element doesn't have a child, but is a child of another node - None if self.index.len() > 0 => { + None if !self.index.is_empty() => { let mut has_finished = false; let mut next_node = None; @@ -101,9 +101,7 @@ impl<'a> Iterator for NodeIntoIterator<'a> { next_node } _ => None, - }; - - result + } } } diff --git a/tests/node_iter.rs b/tests/node_iter.rs index 2d1dfd8..fb935b5 100644 --- a/tests/node_iter.rs +++ b/tests/node_iter.rs @@ -17,8 +17,8 @@ fn it_can_iter_1() -> Result<()> { "}; - let dom = Dom::parse(&html)?; - let root = dom.children.get(0).unwrap().into_iter(); + let dom = Dom::parse(html)?; + let root = dom.children.first().unwrap().into_iter(); let num_li = root.into_iter().fold(0, |mut acc, curr| match curr { Node::Element(e) => { if e.name == "li" { diff --git a/tests/svg.rs b/tests/svg.rs index 2f17f7b..451a835 100644 --- a/tests/svg.rs +++ b/tests/svg.rs @@ -41,5 +41,5 @@ fn it_can_parse_complex_svg() { "# ); - assert!(Dom::parse(&svg).is_ok()); + assert!(Dom::parse(svg).is_ok()); } From 25b7d248abd498f177468a8fa09ba8de8220d296 Mon Sep 17 00:00:00 2001 From: Alex Kazik Date: Sun, 8 Mar 2026 18:35:55 +0100 Subject: [PATCH 04/15] remove unused dependencies --- Cargo.lock | 8 -------- Cargo.toml | 2 -- 2 files changed, 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d4dd04d..10998ef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -390,12 +390,6 @@ dependencies = [ "syn", ] -[[package]] -name = "doc-comment" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "780955b8b195a21ab8e4ac6b60dd1dbdcec1dc6c51c0617964b08c81785e12c9" - [[package]] name = "either" version = "1.15.0" @@ -634,14 +628,12 @@ version = "0.7.0" dependencies = [ "clap 4.5.60", "criterion", - "doc-comment", "indoc", "insta", "pest", "pest_derive", "reqwest", "serde", - "serde_derive", "serde_json", "tempfile", "thiserror", diff --git a/Cargo.toml b/Cargo.toml index 84a8b3d..cd70fa6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,9 +15,7 @@ pest = "2.5.7" pest_derive = "2.5.7" thiserror = "1.0.40" serde = { version = "1.0.159", features = ["derive"] } -serde_derive = "1.0.159" serde_json = "1.0.95" -doc-comment = "0.3.3" [dev-dependencies] indoc = "2.0.1" From 29f9f7b99f874578cabdccb8ca52022ad82fa106 Mon Sep 17 00:00:00 2001 From: Alex Kazik Date: Sun, 8 Mar 2026 21:04:32 +0100 Subject: [PATCH 05/15] export SourceSpan --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index c91b753..fe4cc4f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -106,5 +106,6 @@ pub use crate::dom::Dom; pub use crate::dom::DomVariant; pub use crate::dom::element::{Element, ElementVariant}; pub use crate::dom::node::Node; +pub use crate::dom::span::SourceSpan; pub use crate::error::Error; pub use crate::error::Result; From 422ebc1cfd55333affee43246a1e924b236e4e04 Mon Sep 17 00:00:00 2001 From: Alex Kazik Date: Sun, 8 Mar 2026 21:08:32 +0100 Subject: [PATCH 06/15] make SourceSpan optional --- Cargo.toml | 4 ++++ src/dom/element.rs | 3 +++ src/dom/mod.rs | 31 ++++++++++++++++--------------- src/lib.rs | 1 + 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cd70fa6..4f72fc6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,10 @@ thiserror = "1.0.40" serde = { version = "1.0.159", features = ["derive"] } serde_json = "1.0.95" +[features] +default = ["source-span"] +source-span = [] + [dev-dependencies] indoc = "2.0.1" insta = { version = "1.29.0", features = ["json"] } diff --git a/src/dom/element.rs b/src/dom/element.rs index b2ef576..12266b9 100644 --- a/src/dom/element.rs +++ b/src/dom/element.rs @@ -1,4 +1,5 @@ use super::node::Node; +#[cfg(feature = "source-span")] use super::span::SourceSpan; use serde::{Serialize, Serializer}; use std::collections::{BTreeMap, HashMap}; @@ -45,6 +46,7 @@ pub struct Element { #[serde(skip_serializing_if = "Vec::is_empty")] pub children: Vec, + #[cfg(feature = "source-span")] /// Span of the element in the parsed source #[serde(skip)] pub source_span: SourceSpan, @@ -59,6 +61,7 @@ impl Default for Element { classes: vec![], attributes: HashMap::new(), children: vec![], + #[cfg(feature = "source-span")] source_span: SourceSpan::default(), } } diff --git a/src/dom/mod.rs b/src/dom/mod.rs index 15d738b..b37c56e 100644 --- a/src/dom/mod.rs +++ b/src/dom/mod.rs @@ -10,8 +10,10 @@ use crate::grammar::Grammar; pub mod element; pub mod formatting; pub mod node; +#[cfg(feature = "source-span")] pub mod span; +#[cfg(feature = "source-span")] use crate::dom::span::SourceSpan; use element::{Element, ElementVariant}; use node::Node; @@ -232,22 +234,21 @@ impl Dom { } fn build_node_element(pair: Pair, dom: &mut Dom) -> Result> { - let source_span = { - let pair_span = pair.as_span(); - let (start_line, start_column) = pair_span.start_pos().line_col(); - let (end_line, end_column) = pair_span.end_pos().line_col(); - - SourceSpan::new( - String::from(pair_span.as_str()), - start_line, - end_line, - start_column, - end_column, - ) - }; - let mut element = Element { - source_span, + #[cfg(feature = "source-span")] + source_span: { + let pair_span = pair.as_span(); + let (start_line, start_column) = pair_span.start_pos().line_col(); + let (end_line, end_column) = pair_span.end_pos().line_col(); + + SourceSpan::new( + String::from(pair_span.as_str()), + start_line, + end_line, + start_column, + end_column, + ) + }, ..Element::default() }; diff --git a/src/lib.rs b/src/lib.rs index fe4cc4f..605c91b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -106,6 +106,7 @@ pub use crate::dom::Dom; pub use crate::dom::DomVariant; pub use crate::dom::element::{Element, ElementVariant}; pub use crate::dom::node::Node; +#[cfg(feature = "source-span")] pub use crate::dom::span::SourceSpan; pub use crate::error::Error; pub use crate::error::Result; From e0b056c59cf9701f4bbaf9808462321b9bddd9ab Mon Sep 17 00:00:00 2001 From: Alex Kazik Date: Sun, 8 Mar 2026 19:42:45 +0100 Subject: [PATCH 07/15] use cargo command to run example --- tests/bin.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/bin.rs b/tests/bin.rs index 65d1f6a..7d3ddc7 100644 --- a/tests/bin.rs +++ b/tests/bin.rs @@ -16,7 +16,8 @@ fn it_prints_out_processing_error() -> Result<()> { let mut file = NamedTempFile::new()?; file.write_all(html.as_bytes())?; - let output = Command::new("./target/debug/examples/simple_parser") + let output = Command::new("cargo") + .args(["run", "--example", "simple_parser", "--"]) .arg("-d") .arg(file.path()) .output() From 972a6ac8a8a04d972efeb36f45781cc5465c547c Mon Sep 17 00:00:00 2001 From: Alex Kazik Date: Sun, 8 Mar 2026 19:58:49 +0100 Subject: [PATCH 08/15] use VecMap/VecSet to keep order in attributes and classes This changes some text results as attributes are now kept in original order, and classes are shown as a set (not list). --- src/dom/element.rs | 24 ++- src/dom/mod.rs | 4 +- src/dom/vecmap.rs | 147 ++++++++++++++++++ src/dom/vecset.rs | 99 ++++++++++++ src/lib.rs | 2 + ...arse_multiple_attributes_double_quote.snap | 5 +- ...an_parse_multiple_attributes_no_quote.snap | 5 +- ...arse_multiple_attributes_single_quote.snap | 5 +- ...e_whitespace_does_not_matter_for_keys.snap | 5 +- ...rce_span__it_can_generate_source_span.snap | 7 +- tests/snapshots/svg__it_can_parse_svg.snap | 7 +- 11 files changed, 280 insertions(+), 30 deletions(-) create mode 100644 src/dom/vecmap.rs create mode 100644 src/dom/vecset.rs diff --git a/src/dom/element.rs b/src/dom/element.rs index 12266b9..8c328da 100644 --- a/src/dom/element.rs +++ b/src/dom/element.rs @@ -1,10 +1,10 @@ use super::node::Node; #[cfg(feature = "source-span")] use super::span::SourceSpan; -use serde::{Serialize, Serializer}; -use std::collections::{BTreeMap, HashMap}; +use crate::VecSet; +use crate::dom::vecmap::VecMap; +use serde::Serialize; use std::default::Default; -use std::result::Result; /// Normal: `
` or Void: ``and `` #[derive(Debug, Clone, Serialize, PartialEq)] @@ -17,7 +17,7 @@ pub enum ElementVariant { Void, } -pub type Attributes = HashMap>; +pub type Attributes = VecMap>; /// Most of the parsed html nodes are elements, except for text #[derive(Debug, Clone, Serialize, PartialEq)] @@ -34,13 +34,12 @@ pub struct Element { pub variant: ElementVariant, /// All of the elements attributes, except id and class - #[serde(skip_serializing_if = "HashMap::is_empty")] - #[serde(serialize_with = "ordered_map")] + #[serde(skip_serializing_if = "VecMap::is_empty")] pub attributes: Attributes, /// All of the elements classes - #[serde(skip_serializing_if = "Vec::is_empty")] - pub classes: Vec, + #[serde(skip_serializing_if = "VecSet::is_empty")] + pub classes: VecSet, /// All of the elements child nodes #[serde(skip_serializing_if = "Vec::is_empty")] @@ -58,16 +57,11 @@ impl Default for Element { id: None, name: "".to_string(), variant: ElementVariant::Void, - classes: vec![], - attributes: HashMap::new(), + classes: VecSet::default(), + attributes: VecMap::default(), children: vec![], #[cfg(feature = "source-span")] source_span: SourceSpan::default(), } } } - -fn ordered_map(value: &Attributes, serializer: S) -> Result { - let ordered: BTreeMap<_, _> = value.iter().collect(); - ordered.serialize(serializer) -} diff --git a/src/dom/mod.rs b/src/dom/mod.rs index b37c56e..545f1a1 100644 --- a/src/dom/mod.rs +++ b/src/dom/mod.rs @@ -12,6 +12,8 @@ pub mod formatting; pub mod node; #[cfg(feature = "source-span")] pub mod span; +pub mod vecmap; +pub mod vecset; #[cfg(feature = "source-span")] use crate::dom::span::SourceSpan; @@ -291,7 +293,7 @@ impl Dom { if let Some(classes) = attr_value { let classes = classes.split_whitespace().collect::>(); for class in classes { - element.classes.push(class.to_string()); + element.classes.insert(class.to_string()); } } } diff --git a/src/dom/vecmap.rs b/src/dom/vecmap.rs new file mode 100644 index 0000000..7f39cbd --- /dev/null +++ b/src/dom/vecmap.rs @@ -0,0 +1,147 @@ +use serde::{Serialize, Serializer}; +use std::borrow::Borrow; +use std::fmt::{Debug, Formatter}; +use std::iter::{FromIterator, Map}; +use std::mem; +use std::ops::Index; + +#[derive(Clone, Default, PartialEq, Eq)] +pub struct VecMap(pub Vec<(K, V)>); + +impl VecMap { + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + pub fn iter(&self) -> Iter<'_, K, V> { + Iter(self.0.iter().map(|kv| (&kv.0, &kv.1))) + } + + fn position(&self, key: &Q) -> Option + where + K: Eq + Borrow, + Q: Eq + ?Sized, + { + self.0.iter().position(|(k, _)| k.borrow() == key) + } + + pub fn contains_key(&self, key: &Q) -> bool + where + K: Eq + Borrow, + Q: Eq + ?Sized, + { + self.position(key).is_some() + } + + pub fn insert(&mut self, key: K, mut value: V) -> Option + where + K: Eq, + { + match self.position(&key) { + None => { + self.0.push((key, value)); + None + } + Some(i) => { + mem::swap(&mut value, &mut unsafe { self.0.get_unchecked_mut(i) }.1); + Some(value) + } + } + } + + pub fn get(&self, key: &Q) -> Option<&V> + where + K: Eq + Borrow, + Q: Eq + ?Sized, + { + self.position(key) + .map(|i| &unsafe { self.0.get_unchecked(i) }.1) + } + + pub fn get_mut(&mut self, key: &Q) -> Option<&mut V> + where + K: Eq + Borrow, + Q: Eq + ?Sized, + { + self.position(key) + .map(move |i| &mut unsafe { self.0.get_unchecked_mut(i) }.1) + } + + pub fn remove(&mut self, key: &Q) -> Option + where + K: Eq + Borrow, + Q: Eq + ?Sized, + { + self.position(key).map(|i| self.0.remove(i).1) + } +} + +impl Debug for VecMap +where + K: Debug, + V: Debug, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_map().entries(self.iter()).finish() + } +} + +#[allow(clippy::type_complexity)] +pub struct Iter<'a, K, V>(pub Map, fn(&'a (K, V)) -> (&K, &V)>); + +impl<'a, K, V> Iterator for Iter<'a, K, V> { + type Item = (&'a K, &'a V); + + fn next(&mut self) -> Option { + self.0.next() + } +} + +impl FromIterator<(K, V)> for VecMap { + fn from_iter>(iter: T) -> Self { + Self(Vec::from_iter(iter)) + } +} + +pub struct IntoIter(pub std::vec::IntoIter<(K, V)>); + +impl IntoIterator for VecMap { + type Item = (K, V); + type IntoIter = IntoIter; + + fn into_iter(self) -> Self::IntoIter { + IntoIter(self.0.into_iter()) + } +} + +impl Iterator for IntoIter { + type Item = (K, V); + + fn next(&mut self) -> Option { + self.0.next() + } +} + +impl Index<&Q> for VecMap +where + K: Eq + Borrow, + Q: Eq + ?Sized, +{ + type Output = V; + + fn index(&self, index: &Q) -> &Self::Output { + self.get(index).expect("no entry found for key") + } +} + +impl Serialize for VecMap +where + K: Serialize, + V: Serialize, +{ + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.collect_map(self.iter()) + } +} diff --git a/src/dom/vecset.rs b/src/dom/vecset.rs new file mode 100644 index 0000000..5a55984 --- /dev/null +++ b/src/dom/vecset.rs @@ -0,0 +1,99 @@ +use crate::VecMap; +use serde::{Serialize, Serializer}; +use std::borrow::Borrow; +use std::fmt::{Debug, Formatter}; +use std::iter::{FromIterator, Map}; + +#[derive(Clone, Default, PartialEq, Eq)] +pub struct VecSet(pub VecMap); + +impl VecSet { + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + pub fn iter(&self) -> Iter<'_, K> { + Iter(self.0.iter().map(|(k, _)| k)) + } + + pub fn contains_key(&self, key: &Q) -> bool + where + K: Eq + Borrow, + Q: Eq + ?Sized, + { + self.0.contains_key(key) + } + + pub fn insert(&mut self, key: K) -> bool + where + K: Eq, + { + self.0.insert(key, ()).is_none() + } + + pub fn remove(&mut self, key: &Q) -> bool + where + K: Eq + Borrow, + Q: Eq + ?Sized, + { + self.0.remove(key).is_some() + } +} + +impl Debug for VecSet +where + K: Debug, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_set().entries(self.0.iter()).finish() + } +} + +impl FromIterator for VecSet { + fn from_iter>(iter: T) -> Self { + Self(VecMap::from_iter(iter.into_iter().map(|k| (k, ())))) + } +} + +#[allow(clippy::type_complexity)] +pub struct Iter<'a, K>(pub Map, fn((&'a K, &'a ())) -> &'a K>); + +impl<'a, K> Iterator for Iter<'a, K> { + type Item = &'a K; + + fn next(&mut self) -> Option { + self.0.next() + } +} + +#[allow(clippy::type_complexity)] +pub struct IntoIter(pub Map, fn((K, ())) -> K>); + +impl IntoIterator for VecSet { + type Item = K; + type IntoIter = IntoIter; + + fn into_iter(self) -> Self::IntoIter { + IntoIter(self.0.into_iter().map(|(k, _)| k)) + } +} + +impl Iterator for IntoIter { + type Item = K; + + fn next(&mut self) -> Option { + self.0.next() + } +} + +impl Serialize for VecSet +where + K: Serialize, +{ + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.collect_seq(self.0.iter().map(|(k, _)| k)) + } +} diff --git a/src/lib.rs b/src/lib.rs index 605c91b..68b49b6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -108,5 +108,7 @@ pub use crate::dom::element::{Element, ElementVariant}; pub use crate::dom::node::Node; #[cfg(feature = "source-span")] pub use crate::dom::span::SourceSpan; +pub use crate::dom::vecmap::VecMap; +pub use crate::dom::vecset::VecSet; pub use crate::error::Error; pub use crate::error::Result; diff --git a/tests/snapshots/element_attributes__it_can_parse_multiple_attributes_double_quote.snap b/tests/snapshots/element_attributes__it_can_parse_multiple_attributes_double_quote.snap index a24fbba..8e8a3bc 100644 --- a/tests/snapshots/element_attributes__it_can_parse_multiple_attributes_double_quote.snap +++ b/tests/snapshots/element_attributes__it_can_parse_multiple_attributes_double_quote.snap @@ -1,5 +1,6 @@ --- source: tests/element_attributes.rs +assertion_line: 50 expression: dom --- { @@ -9,9 +10,9 @@ expression: dom "name": "div", "variant": "normal", "attributes": { - "ape": "oh", "cat": "mjau", - "dog": "woff" + "dog": "woff", + "ape": "oh" } } ] diff --git a/tests/snapshots/element_attributes__it_can_parse_multiple_attributes_no_quote.snap b/tests/snapshots/element_attributes__it_can_parse_multiple_attributes_no_quote.snap index a24fbba..87eb6a1 100644 --- a/tests/snapshots/element_attributes__it_can_parse_multiple_attributes_no_quote.snap +++ b/tests/snapshots/element_attributes__it_can_parse_multiple_attributes_no_quote.snap @@ -1,5 +1,6 @@ --- source: tests/element_attributes.rs +assertion_line: 57 expression: dom --- { @@ -9,9 +10,9 @@ expression: dom "name": "div", "variant": "normal", "attributes": { - "ape": "oh", "cat": "mjau", - "dog": "woff" + "dog": "woff", + "ape": "oh" } } ] diff --git a/tests/snapshots/element_attributes__it_can_parse_multiple_attributes_single_quote.snap b/tests/snapshots/element_attributes__it_can_parse_multiple_attributes_single_quote.snap index a24fbba..6cb2b33 100644 --- a/tests/snapshots/element_attributes__it_can_parse_multiple_attributes_single_quote.snap +++ b/tests/snapshots/element_attributes__it_can_parse_multiple_attributes_single_quote.snap @@ -1,5 +1,6 @@ --- source: tests/element_attributes.rs +assertion_line: 36 expression: dom --- { @@ -9,9 +10,9 @@ expression: dom "name": "div", "variant": "normal", "attributes": { - "ape": "oh", "cat": "mjau", - "dog": "woff" + "dog": "woff", + "ape": "oh" } } ] diff --git a/tests/snapshots/element_attributes__it_can_parse_multiple_attributes_where_whitespace_does_not_matter_for_keys.snap b/tests/snapshots/element_attributes__it_can_parse_multiple_attributes_where_whitespace_does_not_matter_for_keys.snap index 8261b08..873ec54 100644 --- a/tests/snapshots/element_attributes__it_can_parse_multiple_attributes_where_whitespace_does_not_matter_for_keys.snap +++ b/tests/snapshots/element_attributes__it_can_parse_multiple_attributes_where_whitespace_does_not_matter_for_keys.snap @@ -1,5 +1,6 @@ --- source: tests/element_attributes.rs +assertion_line: 43 expression: dom --- { @@ -9,9 +10,9 @@ expression: dom "name": "div", "variant": "normal", "attributes": { - "ape": "oh", "cat": "mjau", - "dog": " woff " + "dog": " woff ", + "ape": "oh" } } ] diff --git a/tests/snapshots/source_span__it_can_generate_source_span.snap b/tests/snapshots/source_span__it_can_generate_source_span.snap index 4f630ae..e01a12c 100644 --- a/tests/snapshots/source_span__it_can_generate_source_span.snap +++ b/tests/snapshots/source_span__it_can_generate_source_span.snap @@ -1,5 +1,6 @@ --- source: tests/source_span.rs +assertion_line: 14 expression: dom --- Dom { @@ -11,7 +12,7 @@ Dom { name: "template", variant: Normal, attributes: {}, - classes: [], + classes: {}, children: [ Element( Element { @@ -19,7 +20,7 @@ Dom { name: "h1", variant: Normal, attributes: {}, - classes: [], + classes: {}, children: [ Text( "Header", @@ -40,7 +41,7 @@ Dom { name: "p", variant: Normal, attributes: {}, - classes: [], + classes: {}, children: [ Text( "Paragraph", diff --git a/tests/snapshots/svg__it_can_parse_svg.snap b/tests/snapshots/svg__it_can_parse_svg.snap index 42ec102..1be6938 100644 --- a/tests/snapshots/svg__it_can_parse_svg.snap +++ b/tests/snapshots/svg__it_can_parse_svg.snap @@ -1,5 +1,6 @@ --- source: tests/svg.rs +assertion_line: 15 expression: dom --- { @@ -17,11 +18,11 @@ expression: dom "name": "rect", "variant": "void", "attributes": { + "x": "10", + "y": "10", "height": "100", - "style": "stroke:#ff0000; fill: #0000ff", "width": "100", - "x": "10", - "y": "10" + "style": "stroke:#ff0000; fill: #0000ff" } } ] From 4fc37c1d4a4ea470fbcd6a7e727178bbfbc77b2d Mon Sep 17 00:00:00 2001 From: Alex Kazik Date: Sun, 8 Mar 2026 20:00:51 +0100 Subject: [PATCH 09/15] switch to Cow this allows, thanks to the ownable crate, deep copies while only referencing the original (and other stuff) --- Cargo.lock | 70 ++++++++++++++++++++++++++ Cargo.toml | 1 + examples/get_all_href/main.rs | 6 ++- src/dom/element.rs | 31 ++++++------ src/dom/formatting.rs | 2 +- src/dom/html.rs | 95 +++++++++++++++++++++++++++++++++++ src/dom/mod.rs | 62 +++++++++++++---------- src/dom/node.rs | 64 ++++++++++++++--------- src/dom/span.rs | 14 +++--- src/dom/vecmap.rs | 33 ++++++++++++ src/dom/vecset.rs | 23 +++++++++ src/lib.rs | 1 + tests/element.rs | 4 +- 13 files changed, 330 insertions(+), 76 deletions(-) create mode 100644 src/dom/html.rs diff --git a/Cargo.lock b/Cargo.lock index 10998ef..d6ac08f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -369,6 +369,41 @@ dependencies = [ "typenum", ] +[[package]] +name = "darling" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +dependencies = [ + "darling_core", + "quote", + "syn", +] + [[package]] name = "digest" version = "0.10.7" @@ -630,6 +665,7 @@ dependencies = [ "criterion", "indoc", "insta", + "ownable", "pest", "pest_derive", "reqwest", @@ -811,6 +847,12 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "1.1.0" @@ -1066,6 +1108,34 @@ version = "6.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" +[[package]] +name = "ownable" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be13fa10c77ab9e9dc89fb30d6727f40d740ff2caf256dc28fc1a99efcf585d" +dependencies = [ + "ownable-core", + "ownable-macro", +] + +[[package]] +name = "ownable-core" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc3131271b6bf6f636edd8d1c0762221650c28c37cfe61c151e46ef9b020d38" + +[[package]] +name = "ownable-macro" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c5dc3663cb539af1af233d217a85ff56330c7b212b523128fcd9a5b4bd542ea" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "percent-encoding" version = "2.3.2" diff --git a/Cargo.toml b/Cargo.toml index 4f72fc6..6dfc5f4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ pest_derive = "2.5.7" thiserror = "1.0.40" serde = { version = "1.0.159", features = ["derive"] } serde_json = "1.0.95" +ownable = "1.0.0" [features] default = ["source-span"] diff --git a/examples/get_all_href/main.rs b/examples/get_all_href/main.rs index 607427e..59143f0 100644 --- a/examples/get_all_href/main.rs +++ b/examples/get_all_href/main.rs @@ -1,4 +1,6 @@ use html_parser::{Dom, Node, Result}; +use ownable::traits::ToBorrowed; +use std::ops::Deref; // This example illustrates how to use the library to get all of the anchor-hrefs from a document. @@ -8,13 +10,13 @@ fn main() -> Result<()> { let iter = dom.children.first().unwrap().into_iter(); let hrefs = iter.filter_map(|item| match item { - Node::Element(element) if element.name == "a" => element.attributes["href"].clone(), + Node::Element(element) if element.name == "a" => element.attributes["href"].to_borrowed(), _ => None, }); println!("\nThe following links where found:"); for (index, href) in hrefs.enumerate() { - println!("{}: {}", index + 1, href) + println!("{}: {}", index + 1, href.deref()) } Ok(()) diff --git a/src/dom/element.rs b/src/dom/element.rs index 8c328da..484df21 100644 --- a/src/dom/element.rs +++ b/src/dom/element.rs @@ -1,9 +1,11 @@ use super::node::Node; #[cfg(feature = "source-span")] use super::span::SourceSpan; -use crate::VecSet; use crate::dom::vecmap::VecMap; +use crate::{Attribute, VecSet}; +use ownable::{IntoOwned, ToBorrowed, ToOwned}; use serde::Serialize; +use std::borrow::Cow; use std::default::Default; /// Normal: `
` or Void: ``and `` @@ -17,48 +19,49 @@ pub enum ElementVariant { Void, } -pub type Attributes = VecMap>; +pub type Attributes<'a> = VecMap, Option>>; /// Most of the parsed html nodes are elements, except for text -#[derive(Debug, Clone, Serialize, PartialEq)] +#[derive(Debug, Serialize, PartialEq, IntoOwned, ToBorrowed, ToOwned)] #[serde(rename_all = "camelCase")] -pub struct Element { +pub struct Element<'a> { /// The id of the element #[serde(skip_serializing_if = "Option::is_none")] - pub id: Option, + pub id: Option>, /// The name / tag of the element - pub name: String, + pub name: Cow<'a, str>, /// The element variant, if it is of type void or not + #[ownable(clone)] pub variant: ElementVariant, /// All of the elements attributes, except id and class #[serde(skip_serializing_if = "VecMap::is_empty")] - pub attributes: Attributes, + pub attributes: Attributes<'a>, /// All of the elements classes #[serde(skip_serializing_if = "VecSet::is_empty")] - pub classes: VecSet, + pub classes: VecSet>, /// All of the elements child nodes #[serde(skip_serializing_if = "Vec::is_empty")] - pub children: Vec, + pub children: Vec>, #[cfg(feature = "source-span")] /// Span of the element in the parsed source #[serde(skip)] - pub source_span: SourceSpan, + pub source_span: SourceSpan<'a>, } -impl Default for Element { +impl Default for Element<'static> { fn default() -> Self { Self { id: None, - name: "".to_string(), + name: "".into(), variant: ElementVariant::Void, - classes: VecSet::default(), - attributes: VecMap::default(), + classes: Default::default(), + attributes: Default::default(), children: vec![], #[cfg(feature = "source-span")] source_span: SourceSpan::default(), diff --git a/src/dom/formatting.rs b/src/dom/formatting.rs index 2dfe125..b41b678 100644 --- a/src/dom/formatting.rs +++ b/src/dom/formatting.rs @@ -5,7 +5,7 @@ use pest::error::Error as PestError; /// This function abstracts the formatting of errors away from the core logic inside parser, /// so that the file is easier to read. -pub fn error_msg(error: PestError) -> Result { +pub fn error_msg(error: PestError) -> Result> { let message = error.renamed_rules(|rule| match *rule { Rule::EOI => "end of input".to_string(), Rule::doctype => "doctype element".to_string(), diff --git a/src/dom/html.rs b/src/dom/html.rs new file mode 100644 index 0000000..2467ed9 --- /dev/null +++ b/src/dom/html.rs @@ -0,0 +1,95 @@ +use ownable::{IntoOwned, ToBorrowed, ToOwned}; +use serde::Serialize; +use std::borrow::{Borrow, Cow}; +use std::fmt::{Debug, Formatter}; +use std::ops::Deref; + +#[derive(Default, Debug, Serialize, PartialEq, Eq, IntoOwned, ToBorrowed, ToOwned)] +#[serde(transparent)] +pub struct Attribute<'a>(pub Cow<'a, str>); + +impl<'a> Attribute<'a> { + pub fn as_str(&self) -> &str { + self.0.borrow() + } +} + +impl>> From for Attribute<'static> { + fn from(value: S) -> Self { + Self(value.into()) + } +} + +impl<'a> Deref for Attribute<'a> { + type Target = str; + + fn deref(&self) -> &Self::Target { + self.as_str() + } +} + +impl<'a> PartialEq for Attribute<'a> { + fn eq(&self, other: &str) -> bool { + self.deref().eq(other) + } +} + +impl<'a> PartialEq<&str> for Attribute<'a> { + fn eq(&self, other: &&str) -> bool { + self.deref().eq(*other) + } +} + +impl<'a> PartialEq for &Attribute<'a> { + fn eq(&self, other: &str) -> bool { + (*self).deref().eq(other) + } +} + +#[derive(Default, Serialize, PartialEq, Eq, IntoOwned, ToBorrowed, ToOwned)] +#[serde(transparent)] +pub struct Text<'a>(pub Cow<'a, str>); + +impl<'a> Text<'a> { + pub fn as_str(&self) -> &str { + self.0.borrow() + } +} + +impl Debug for Text<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + self.as_str().fmt(f) + } +} + +impl>> From for Text<'static> { + fn from(value: S) -> Self { + Self(value.into()) + } +} + +impl<'a> Deref for Text<'a> { + type Target = str; + + fn deref(&self) -> &Self::Target { + self.as_str() + } +} + +impl<'a> PartialEq for Text<'a> { + fn eq(&self, other: &str) -> bool { + self.deref().eq(other) + } +} + +impl<'a> PartialEq<&str> for Text<'a> { + fn eq(&self, other: &&str) -> bool { + self.deref().eq(*other) + } +} + +impl<'a> PartialEq for &Text<'a> { + fn eq(&self, other: &str) -> bool { + (*self).deref().eq(other) + } +} diff --git a/src/dom/mod.rs b/src/dom/mod.rs index 545f1a1..527e6a8 100644 --- a/src/dom/mod.rs +++ b/src/dom/mod.rs @@ -1,14 +1,17 @@ -use crate::Result; +use ownable::{IntoOwned, ToBorrowed, ToOwned}; use pest::{Parser, iterators::Pair, iterators::Pairs}; use serde::Serialize; +use std::borrow::Cow; use std::default::Default; use crate::Rule; use crate::error::Error; use crate::grammar::Grammar; +use crate::{Attribute, Result, Text}; pub mod element; pub mod formatting; +pub mod html; pub mod node; #[cfg(feature = "source-span")] pub mod span; @@ -21,7 +24,7 @@ use element::{Element, ElementVariant}; use node::Node; /// Document, DocumentFragment or Empty -#[derive(Debug, Clone, PartialEq, Serialize)] +#[derive(Debug, Clone, Copy, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub enum DomVariant { /// This means that the parsed html had the representation of an html document. The doctype is optional but a document should only have one root node with the name of html. @@ -47,22 +50,24 @@ pub enum DomVariant { } /// **The main struct** & the result of the parsed html -#[derive(Debug, Clone, Serialize, PartialEq)] +#[derive(Debug, Serialize, PartialEq, ToBorrowed, ToOwned, IntoOwned)] #[serde(rename_all = "camelCase")] -pub struct Dom { +pub struct Dom<'a> { /// The type of the tree that was parsed + #[ownable(clone)] pub tree_type: DomVariant, /// All of the root children in the tree #[serde(skip_serializing_if = "Vec::is_empty")] - pub children: Vec, + pub children: Vec>, /// A collection of all errors during parsing #[serde(skip_serializing)] + #[ownable(clone)] pub errors: Vec, } -impl Default for Dom { +impl<'a> Default for Dom<'a> { fn default() -> Self { Self { tree_type: DomVariant::Empty, @@ -72,8 +77,8 @@ impl Default for Dom { } } -impl Dom { - pub fn parse(input: &str) -> Result { +impl<'a> Dom<'a> { + pub fn parse(input: &'a str) -> Result { let pairs = match Grammar::parse(Rule::html, input) { Ok(pairs) => pairs, Err(error) => return formatting::error_msg(error), @@ -89,7 +94,7 @@ impl Dom { Ok(serde_json::to_string_pretty(self)?) } - fn build_dom(pairs: Pairs) -> Result { + fn build_dom(pairs: Pairs<'a, Rule>) -> Result { let mut dom = Self::default(); // NOTE: The logic is roughly as follows: @@ -134,9 +139,9 @@ impl Dom { if dom.tree_type == DomVariant::Empty { dom.tree_type = DomVariant::DocumentFragment; } - let text = pair.as_str().to_string(); + let text = pair.as_str(); if !text.trim().is_empty() { - dom.children.push(Node::Text(text)); + dom.children.push(Node::Text(Text(text.into()))); } } @@ -144,7 +149,7 @@ impl Dom { // until the next phase (validation). Rule::node_comment => { dom.children - .push(Node::Comment(pair.into_inner().as_str().to_string())); + .push(Node::Comment(Text(pair.into_inner().as_str().into()))); } // Ignore 'end of input', which then allows the catch-all unreachable!() arm to @@ -235,7 +240,7 @@ impl Dom { Ok(dom) } - fn build_node_element(pair: Pair, dom: &mut Dom) -> Result> { + fn build_node_element(pair: Pair<'a, Rule>, dom: &mut Dom) -> Result>> { let mut element = Element { #[cfg(feature = "source-span")] source_span: { @@ -244,7 +249,7 @@ impl Dom { let (end_line, end_column) = pair_span.end_pos().line_col(); SourceSpan::new( - String::from(pair_span.as_str()), + pair_span.as_str(), start_line, end_line, start_column, @@ -269,36 +274,39 @@ impl Dom { } } Rule::node_text | Rule::el_raw_text_content => { - let text = pair.as_str().to_string(); + let text = pair.as_str(); if !text.trim().is_empty() { - element.children.push(Node::Text(text)); + element.children.push(Node::Text(Text(text.into()))); } } Rule::node_comment => { element .children - .push(Node::Comment(pair.into_inner().as_str().to_string())); + .push(Node::Comment(Text(pair.into_inner().as_str().into()))); } // TODO: To enable some kind of validation we should probably align this with // https://html.spec.whatwg.org/multipage/syntax.html#elements-2 // Also see element variants Rule::el_name | Rule::el_void_name | Rule::el_raw_text_name => { - element.name = pair.as_str().to_string(); + element.name = pair.as_str().into(); } Rule::attr => match Self::build_attribute(pair.into_inner()) { Ok((attr_key, attr_value)) => { - match attr_key.as_str() { - "id" => element.id = attr_value, + match attr_key { + "id" => element.id = attr_value.map(|s| Attribute(s.into())), "class" => { if let Some(classes) = attr_value { let classes = classes.split_whitespace().collect::>(); for class in classes { - element.classes.insert(class.to_string()); + element.classes.insert(Attribute(class.into())); } } } _ => { - element.attributes.insert(attr_key, attr_value); + element.attributes.insert( + Cow::Borrowed(attr_key), + attr_value.map(|s| Attribute(s.into())), + ); } }; } @@ -327,21 +335,21 @@ impl Dom { } } - fn build_attribute(pairs: Pairs) -> Result<(String, Option)> { - let mut attribute = ("".to_string(), None); + fn build_attribute(pairs: Pairs<'_, Rule>) -> Result<(&str, Option<&str>)> { + let mut attribute = ("", None); for pair in pairs { match pair.as_rule() { Rule::attr_key => { - attribute.0 = pair.as_str().trim().to_string(); + attribute.0 = pair.as_str().trim(); } Rule::attr_non_quoted => { - attribute.1 = Some(pair.as_str().trim().to_string()); + attribute.1 = Some(pair.as_str().trim()); } Rule::attr_quoted => { let inner_pair = pair.into_inner().next().expect("attribute value"); match inner_pair.as_rule() { - Rule::attr_value => attribute.1 = Some(inner_pair.as_str().to_string()), + Rule::attr_value => attribute.1 = Some(inner_pair.as_str()), _ => { return Err(Error::Parsing(format!( "Failed to parse attr value: {:?}", diff --git a/src/dom/node.rs b/src/dom/node.rs index ade46ae..fc17ee8 100644 --- a/src/dom/node.rs +++ b/src/dom/node.rs @@ -1,39 +1,55 @@ use super::element::Element; +use crate::Text; +use ownable::{IntoOwned, ToBorrowed, ToOwned}; use serde::Serialize; -#[derive(Debug, Clone, Serialize, PartialEq)] +#[derive(Debug, Serialize, PartialEq, IntoOwned, ToBorrowed, ToOwned)] #[serde(untagged)] -pub enum Node { - Text(String), - Element(Element), - Comment(String), +pub enum Node<'a> { + Text(Text<'a>), + Element(Element<'a>), + Comment(Text<'a>), } -impl Node { - pub fn text(&self) -> Option<&str> { +impl<'a> Node<'a> { + pub fn text(&self) -> Option<&Text<'a>> { match self { - Node::Text(t) => Some(t.as_str()), + Node::Text(t) => Some(t), _ => None, } } - pub fn element(&self) -> Option<&Element> { + pub fn text_str(&self) -> Option<&str> { + match self { + Node::Text(t) => Some(t), + _ => None, + } + } + + pub fn element(&self) -> Option<&Element<'a>> { match self { Node::Element(e) => Some(e), _ => None, } } - pub fn comment(&self) -> Option<&str> { + pub fn comment(&self) -> Option<&Text<'a>> { + match self { + Node::Comment(t) => Some(t), + _ => None, + } + } + + pub fn comment_str(&self) -> Option<&str> { match self { - Node::Comment(t) => Some(t.as_str()), + Node::Comment(t) => Some(t), _ => None, } } } -impl<'a> IntoIterator for &'a Node { - type Item = &'a Node; +impl<'a> IntoIterator for &'a Node<'a> { + type Item = &'a Node<'a>; type IntoIter = NodeIntoIterator<'a>; fn into_iter(self) -> Self::IntoIter { @@ -45,13 +61,13 @@ impl<'a> IntoIterator for &'a Node { } pub struct NodeIntoIterator<'a> { - node: &'a Node, + node: &'a Node<'a>, // We add/remove to this vec each time we go up/down a node three - index: Vec<(usize, &'a Node)>, + index: Vec<(usize, &'a Node<'a>)>, } impl<'a> Iterator for NodeIntoIterator<'a> { - type Item = &'a Node; + type Item = &'a Node<'a>; fn next(&mut self) -> Option { // Get first child @@ -111,22 +127,22 @@ mod tests { #[test] fn node_utillity_functions() { - let node = Node::Text("test".to_string()); + let node = Node::Text("test".into()); - assert_eq!(node.text(), Some("test")); + assert_eq!(node.text_str(), Some("test")); assert_eq!(node.element(), None); - assert_eq!(node.comment(), None); + assert_eq!(node.comment_str(), None); let node = Node::Element(Element::default()); - assert_eq!(node.text(), None); + assert_eq!(node.text_str(), None); assert_eq!(node.element(), Some(&Element::default())); - assert_eq!(node.comment(), None); + assert_eq!(node.comment_str(), None); - let node = Node::Comment("test".to_string()); + let node = Node::Comment("test".into()); - assert_eq!(node.text(), None); + assert_eq!(node.text_str(), None); assert_eq!(node.element(), None); - assert_eq!(node.comment(), Some("test")); + assert_eq!(node.comment_str(), Some("test")); } } diff --git a/src/dom/span.rs b/src/dom/span.rs index 3b1f943..239cd55 100644 --- a/src/dom/span.rs +++ b/src/dom/span.rs @@ -1,26 +1,28 @@ +use ownable::{IntoOwned, ToBorrowed, ToOwned}; use serde::Serialize; +use std::borrow::Cow; /// Span of the information in the parsed source. -#[derive(Debug, Default, Clone, Serialize, PartialEq)] +#[derive(Debug, Default, Clone, Serialize, PartialEq, IntoOwned, ToBorrowed, ToOwned)] #[serde(rename_all = "camelCase")] -pub struct SourceSpan { - pub text: String, +pub struct SourceSpan<'a> { + pub text: Cow<'a, str>, pub start_line: usize, pub end_line: usize, pub start_column: usize, pub end_column: usize, } -impl SourceSpan { +impl<'a> SourceSpan<'a> { pub fn new( - text: String, + text: &'a str, start_line: usize, end_line: usize, start_column: usize, end_column: usize, ) -> Self { Self { - text, + text: text.into(), start_line, end_line, start_column, diff --git a/src/dom/vecmap.rs b/src/dom/vecmap.rs index 7f39cbd..160c663 100644 --- a/src/dom/vecmap.rs +++ b/src/dom/vecmap.rs @@ -1,3 +1,4 @@ +use ownable::traits::{IntoOwned, ToBorrowed, ToOwned}; use serde::{Serialize, Serializer}; use std::borrow::Borrow; use std::fmt::{Debug, Formatter}; @@ -121,6 +122,38 @@ impl Iterator for IntoIter { } } +impl<'a, K: ToBorrowed<'a>, V: ToBorrowed<'a>> ToBorrowed<'a> for VecMap { + fn to_borrowed(&'a self) -> Self { + VecMap( + self.iter() + .map(|(k, v)| (k.to_borrowed(), v.to_borrowed())) + .collect(), + ) + } +} + +impl ToOwned for VecMap { + type Owned = VecMap; + fn to_owned(&self) -> Self::Owned { + VecMap( + self.iter() + .map(|(k, v)| (k.to_owned(), v.to_owned())) + .collect(), + ) + } +} + +impl IntoOwned for VecMap { + type Owned = VecMap; + fn into_owned(self) -> Self::Owned { + VecMap( + self.into_iter() + .map(|(k, v)| (k.into_owned(), v.into_owned())) + .collect(), + ) + } +} + impl Index<&Q> for VecMap where K: Eq + Borrow, diff --git a/src/dom/vecset.rs b/src/dom/vecset.rs index 5a55984..88cc692 100644 --- a/src/dom/vecset.rs +++ b/src/dom/vecset.rs @@ -1,4 +1,5 @@ use crate::VecMap; +use ownable::traits::{IntoOwned, ToBorrowed, ToOwned}; use serde::{Serialize, Serializer}; use std::borrow::Borrow; use std::fmt::{Debug, Formatter}; @@ -86,6 +87,28 @@ impl Iterator for IntoIter { } } +impl<'a, K: ToBorrowed<'a>> ToBorrowed<'a> for VecSet { + fn to_borrowed(&'a self) -> Self { + VecSet(VecMap(self.iter().map(|k| (k.to_borrowed(), ())).collect())) + } +} + +impl ToOwned for VecSet { + type Owned = VecSet; + fn to_owned(&self) -> Self::Owned { + VecSet(VecMap(self.iter().map(|k| (k.to_owned(), ())).collect())) + } +} + +impl IntoOwned for VecSet { + type Owned = VecSet; + fn into_owned(self) -> Self::Owned { + VecSet(VecMap( + self.into_iter().map(|k| (k.into_owned(), ())).collect(), + )) + } +} + impl Serialize for VecSet where K: Serialize, diff --git a/src/lib.rs b/src/lib.rs index 68b49b6..ead65d9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -105,6 +105,7 @@ use grammar::Rule; pub use crate::dom::Dom; pub use crate::dom::DomVariant; pub use crate::dom::element::{Element, ElementVariant}; +pub use crate::dom::html::{Attribute, Text}; pub use crate::dom::node::Node; #[cfg(feature = "source-span")] pub use crate::dom::span::SourceSpan; diff --git a/tests/element.rs b/tests/element.rs index b1719a1..b75f242 100644 --- a/tests/element.rs +++ b/tests/element.rs @@ -201,7 +201,7 @@ fn it_can_clone_node() { " ); let dom = Dom::parse(html).unwrap(); - let one = dom.children[0].clone(); + let one = dom.children[0].to_owned(); assert_json_snapshot!(one); } #[test] @@ -219,7 +219,7 @@ fn it_can_clone_dom() { " ); let dom = Dom::parse(html).unwrap(); - let dom_clone = dom.clone(); + let dom_clone = dom.to_owned(); assert_eq!(dom, dom_clone); } From 6141f0d6adc044ccc22ecfba522ef6baab334eca Mon Sep 17 00:00:00 2001 From: Alex Kazik Date: Tue, 10 Mar 2026 18:16:50 +0100 Subject: [PATCH 10/15] function to walk over all elements (recursive) --- src/dom/element.rs | 13 ++ src/dom/mod.rs | 14 +++ src/dom/node.rs | 14 +++ src/dom/walk.rs | 304 +++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 5 files changed, 346 insertions(+) create mode 100644 src/dom/walk.rs diff --git a/src/dom/element.rs b/src/dom/element.rs index 484df21..207b76e 100644 --- a/src/dom/element.rs +++ b/src/dom/element.rs @@ -2,6 +2,7 @@ use super::node::Node; #[cfg(feature = "source-span")] use super::span::SourceSpan; use crate::dom::vecmap::VecMap; +use crate::dom::walk::Children; use crate::{Attribute, VecSet}; use ownable::{IntoOwned, ToBorrowed, ToOwned}; use serde::Serialize; @@ -68,3 +69,15 @@ impl Default for Element<'static> { } } } + +impl<'a> Children<'a> for Element<'a> { + #[inline] + fn children(&self) -> &[Node<'a>] { + self.children.as_slice() + } + + #[inline] + fn children_mut(&mut self) -> &mut [Node<'a>] { + self.children.as_mut_slice() + } +} diff --git a/src/dom/mod.rs b/src/dom/mod.rs index 527e6a8..88b9469 100644 --- a/src/dom/mod.rs +++ b/src/dom/mod.rs @@ -17,9 +17,11 @@ pub mod node; pub mod span; pub mod vecmap; pub mod vecset; +pub mod walk; #[cfg(feature = "source-span")] use crate::dom::span::SourceSpan; +use crate::dom::walk::Children; use element::{Element, ElementVariant}; use node::Node; @@ -369,3 +371,15 @@ impl<'a> Dom<'a> { Ok(attribute) } } + +impl<'a> Children<'a> for Dom<'a> { + #[inline] + fn children(&self) -> &[Node<'a>] { + self.children.as_slice() + } + + #[inline] + fn children_mut(&mut self) -> &mut [Node<'a>] { + self.children.as_mut_slice() + } +} diff --git a/src/dom/node.rs b/src/dom/node.rs index fc17ee8..0b2f193 100644 --- a/src/dom/node.rs +++ b/src/dom/node.rs @@ -1,7 +1,9 @@ use super::element::Element; use crate::Text; +use crate::dom::walk::Children; use ownable::{IntoOwned, ToBorrowed, ToOwned}; use serde::Serialize; +use std::array; #[derive(Debug, Serialize, PartialEq, IntoOwned, ToBorrowed, ToOwned)] #[serde(untagged)] @@ -48,6 +50,18 @@ impl<'a> Node<'a> { } } +impl<'a> Children<'a> for Node<'a> { + #[inline] + fn children(&self) -> &[Node<'a>] { + array::from_ref(self) + } + + #[inline] + fn children_mut(&mut self) -> &mut [Node<'a>] { + array::from_mut(self) + } +} + impl<'a> IntoIterator for &'a Node<'a> { type Item = &'a Node<'a>; type IntoIter = NodeIntoIterator<'a>; diff --git a/src/dom/walk.rs b/src/dom/walk.rs new file mode 100644 index 0000000..b0f04e8 --- /dev/null +++ b/src/dom/walk.rs @@ -0,0 +1,304 @@ +use crate::{Element, Node, Text}; +use std::ops::ControlFlow; + +pub enum WalkResult { + /// Continue walking. + Continue, + /// Skip the children of this element. + Skip, + /// Break walking. + Break(T), +} + +#[allow(async_fn_in_trait)] +pub trait Children<'a> { + fn children(&self) -> &[Node<'a>]; + + #[inline] + fn for_each_element(&mut self, mut f: F) + where + F: FnMut(&Element<'a>) -> WalkResult<()>, + { + let _ = inner_for_each_element(self.children(), &mut f); + } + + #[inline] + async fn for_each_element_async(&mut self, mut f: F) + where + F: AsyncFnMut(&Element<'a>) -> WalkResult<()>, + { + let _ = inner_for_each_element_async(self.children(), &mut f).await; + } + + #[inline] + fn for_each_element_brk(&mut self, mut f: F) -> ControlFlow + where + F: FnMut(&Element<'a>) -> WalkResult, + { + inner_for_each_element(self.children(), &mut f) + } + + #[inline] + async fn for_each_element_async_brk(&mut self, mut f: F) -> ControlFlow + where + F: AsyncFnMut(&Element<'a>) -> WalkResult, + { + inner_for_each_element_async(self.children(), &mut f).await + } + + #[inline] + fn for_each_text(&mut self, mut f: F) + where + F: FnMut(&Text<'a>) -> WalkResult<()>, + { + let _ = inner_for_each_text(self.children(), &mut f); + } + + #[inline] + async fn for_each_text_async(&mut self, mut f: F) + where + F: AsyncFnMut(&Text<'a>) -> WalkResult<()>, + { + let _ = inner_for_each_text_async(self.children(), &mut f).await; + } + + #[inline] + fn for_each_text_brk(&mut self, mut f: F) -> ControlFlow + where + F: FnMut(&Text<'a>) -> WalkResult, + { + inner_for_each_text(self.children(), &mut f) + } + + #[inline] + async fn for_each_text_async_brk(&mut self, mut f: F) -> ControlFlow + where + F: AsyncFnMut(&Text<'a>) -> WalkResult, + { + inner_for_each_text_async(self.children(), &mut f).await + } + + fn children_mut(&mut self) -> &mut [Node<'a>]; + + #[inline] + fn for_each_element_mut(&mut self, mut f: F) + where + F: FnMut(&mut Element<'a>) -> WalkResult<()>, + { + let _ = inner_for_each_element_mut(self.children_mut(), &mut f); + } + + #[inline] + async fn for_each_element_mut_async(&mut self, mut f: F) + where + F: AsyncFnMut(&mut Element<'a>) -> WalkResult<()>, + { + let _ = inner_for_each_element_mut_async(self.children_mut(), &mut f).await; + } + + #[inline] + fn for_each_element_mut_brk(&mut self, mut f: F) -> ControlFlow + where + F: FnMut(&mut Element<'a>) -> WalkResult, + { + inner_for_each_element_mut(self.children_mut(), &mut f) + } + + #[inline] + async fn for_each_element_mut_async_brk(&mut self, mut f: F) -> ControlFlow + where + F: AsyncFnMut(&mut Element<'a>) -> WalkResult, + { + inner_for_each_element_mut_async(self.children_mut(), &mut f).await + } + + #[inline] + fn for_each_text_mut(&mut self, mut f: F) + where + F: FnMut(&mut Text<'a>) -> WalkResult<()>, + { + let _ = inner_for_each_text_mut(self.children_mut(), &mut f); + } + + #[inline] + async fn for_each_text_mut_async(&mut self, mut f: F) + where + F: AsyncFnMut(&mut Text<'a>) -> WalkResult<()>, + { + let _ = inner_for_each_text_mut_async(self.children_mut(), &mut f).await; + } + + #[inline] + fn for_each_text_mut_brk(&mut self, mut f: F) -> ControlFlow + where + F: FnMut(&mut Text<'a>) -> WalkResult, + { + inner_for_each_text_mut(self.children_mut(), &mut f) + } + + #[inline] + async fn for_each_text_mut_async_brk(&mut self, mut f: F) -> ControlFlow + where + F: AsyncFnMut(&mut Text<'a>) -> WalkResult, + { + inner_for_each_text_mut_async(self.children_mut(), &mut f).await + } +} + +fn inner_for_each_element<'a, F, T>(tree: &[Node<'a>], f: &mut F) -> ControlFlow +where + F: FnMut(&Element<'a>) -> WalkResult, +{ + for n in tree { + if let Node::Element(e) = n { + match f(e) { + WalkResult::Continue => { + inner_for_each_element(e.children.as_slice(), f)?; + } + WalkResult::Skip => (), + WalkResult::Break(t) => return ControlFlow::Break(t), + } + } + } + ControlFlow::Continue(()) +} + +async fn inner_for_each_element_async<'a, F, T>(tree: &[Node<'a>], f: &mut F) -> ControlFlow +where + F: AsyncFnMut(&Element<'a>) -> WalkResult, +{ + for n in tree { + if let Node::Element(e) = n { + match f(e).await { + WalkResult::Continue => { + Box::pin(inner_for_each_element_async(e.children.as_slice(), f)).await?; + } + WalkResult::Skip => (), + WalkResult::Break(t) => return ControlFlow::Break(t), + } + } + } + ControlFlow::Continue(()) +} + +fn inner_for_each_text<'a, F, T>(tree: &[Node<'a>], f: &mut F) -> ControlFlow +where + F: FnMut(&Text<'a>) -> WalkResult, +{ + for n in tree { + match n { + Node::Text(t) => { + if let WalkResult::Break(t) = f(t) { + return ControlFlow::Break(t); + } + } + Node::Element(e) => inner_for_each_text(e.children.as_slice(), f)?, + Node::Comment(_) => {} + } + } + ControlFlow::Continue(()) +} + +async fn inner_for_each_text_async<'a, F, T>(tree: &[Node<'a>], f: &mut F) -> ControlFlow +where + F: AsyncFnMut(&Text<'a>) -> WalkResult, +{ + for n in tree { + match n { + Node::Text(t) => { + if let WalkResult::Break(t) = f(t).await { + return ControlFlow::Break(t); + } + } + Node::Element(e) => { + Box::pin(inner_for_each_text_async(e.children.as_slice(), f)).await? + } + Node::Comment(_) => {} + } + } + ControlFlow::Continue(()) +} + +fn inner_for_each_element_mut<'a, F, T>(tree: &mut [Node<'a>], f: &mut F) -> ControlFlow +where + F: FnMut(&mut Element<'a>) -> WalkResult, +{ + for n in tree { + if let Node::Element(e) = n { + match f(e) { + WalkResult::Continue => { + inner_for_each_element_mut(e.children.as_mut_slice(), f)?; + } + WalkResult::Skip => (), + WalkResult::Break(t) => return ControlFlow::Break(t), + } + } + } + ControlFlow::Continue(()) +} + +async fn inner_for_each_element_mut_async<'a, F, T>( + tree: &mut [Node<'a>], + f: &mut F, +) -> ControlFlow +where + F: AsyncFnMut(&mut Element<'a>) -> WalkResult, +{ + for n in tree { + if let Node::Element(e) = n { + match f(e).await { + WalkResult::Continue => { + Box::pin(inner_for_each_element_mut_async( + e.children.as_mut_slice(), + f, + )) + .await?; + } + WalkResult::Skip => (), + WalkResult::Break(t) => return ControlFlow::Break(t), + } + } + } + ControlFlow::Continue(()) +} + +fn inner_for_each_text_mut<'a, F, T>(tree: &mut [Node<'a>], f: &mut F) -> ControlFlow +where + F: FnMut(&mut Text<'a>) -> WalkResult, +{ + for n in tree { + match n { + Node::Text(t) => { + if let WalkResult::Break(t) = f(t) { + return ControlFlow::Break(t); + } + } + Node::Element(e) => inner_for_each_text_mut(e.children.as_mut_slice(), f)?, + Node::Comment(_) => {} + } + } + ControlFlow::Continue(()) +} + +async fn inner_for_each_text_mut_async<'a, F, T>( + tree: &mut [Node<'a>], + f: &mut F, +) -> ControlFlow +where + F: AsyncFnMut(&mut Text<'a>) -> WalkResult, +{ + for n in tree { + match n { + Node::Text(t) => { + if let WalkResult::Break(t) = f(t).await { + return ControlFlow::Break(t); + } + } + Node::Element(e) => { + Box::pin(inner_for_each_text_mut_async(e.children.as_mut_slice(), f)).await? + } + Node::Comment(_) => {} + } + } + ControlFlow::Continue(()) +} diff --git a/src/lib.rs b/src/lib.rs index ead65d9..2f0ea45 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -111,5 +111,6 @@ pub use crate::dom::node::Node; pub use crate::dom::span::SourceSpan; pub use crate::dom::vecmap::VecMap; pub use crate::dom::vecset::VecSet; +pub use crate::dom::walk::{Children, WalkResult}; pub use crate::error::Error; pub use crate::error::Result; From fc7c7e77c29e619c85324125c52153f0cf4e6621 Mon Sep 17 00:00:00 2001 From: Alex Kazik Date: Wed, 11 Mar 2026 08:59:30 +0100 Subject: [PATCH 11/15] to html --- src/dom/element.rs | 58 +++++++++++++++++++++++++++++++++++++++++++++- src/dom/mod.rs | 13 +++++++++++ src/dom/node.rs | 30 ++++++++++++++++++++++++ 3 files changed, 100 insertions(+), 1 deletion(-) diff --git a/src/dom/element.rs b/src/dom/element.rs index 207b76e..a8e7a1b 100644 --- a/src/dom/element.rs +++ b/src/dom/element.rs @@ -1,4 +1,4 @@ -use super::node::Node; +use super::node::{Node, write_html_list}; #[cfg(feature = "source-span")] use super::span::SourceSpan; use crate::dom::vecmap::VecMap; @@ -81,3 +81,59 @@ impl<'a> Children<'a> for Element<'a> { self.children.as_mut_slice() } } + +impl Element<'_> { + #[inline(always)] + pub fn to_html(&self) -> String { + let mut result = String::new(); + self.write_html(&mut result); + result + } + + pub fn write_html(&self, writer: &mut String) { + let e = self; + + writer.push('<'); + writer.push_str(&e.name); + if !e.classes.is_empty() { + writer.push_str(" class=\""); + writer.push_str( + &e.classes + .iter() + .map(|c| c.as_str()) + .collect::>() + .join(" "), + ); + writer.push('"'); + } + if let Some(id) = &e.id { + writer.push(' '); + writer.push_str("id"); + writer.push('='); + writer.push('"'); + writer.push_str(id); + writer.push('"'); + } + for (k, v) in e.attributes.iter() { + writer.push(' '); + writer.push_str(k); + if let Some(v) = v { + writer.push('='); + writer.push('"'); + writer.push_str(v); + writer.push('"'); + } + } + if e.variant == ElementVariant::Normal { + writer.push('>'); + write_html_list(writer, &e.children); + writer.push('<'); + writer.push('/'); + writer.push_str(&e.name); + writer.push('>'); + } else { + writer.push('/'); + writer.push('>'); + } + } +} diff --git a/src/dom/mod.rs b/src/dom/mod.rs index 88b9469..1ad424c 100644 --- a/src/dom/mod.rs +++ b/src/dom/mod.rs @@ -19,6 +19,7 @@ pub mod vecmap; pub mod vecset; pub mod walk; +use crate::dom::node::write_html_list; #[cfg(feature = "source-span")] use crate::dom::span::SourceSpan; use crate::dom::walk::Children; @@ -370,6 +371,18 @@ impl<'a> Dom<'a> { } Ok(attribute) } + + #[inline(always)] + pub fn to_html(&self) -> String { + let mut result = String::new(); + self.write_html(&mut result); + result + } + + #[inline(always)] + pub fn write_html(&self, writer: &mut String) { + write_html_list(writer, self.children.as_slice()); + } } impl<'a> Children<'a> for Dom<'a> { diff --git a/src/dom/node.rs b/src/dom/node.rs index 0b2f193..01a1145 100644 --- a/src/dom/node.rs +++ b/src/dom/node.rs @@ -62,6 +62,36 @@ impl<'a> Children<'a> for Node<'a> { } } +impl Node<'_> { + #[inline] + pub fn to_html(&self) -> String { + let mut result = String::new(); + self.write_html(&mut result); + result + } + + #[inline] + pub fn write_html(&self, writer: &mut String) { + match self { + Node::Text(s) => { + writer.push_str(s); + } + Node::Element(e) => e.write_html(writer), + Node::Comment(c) => { + writer.push_str(""); + } + } + } +} + +pub(crate) fn write_html_list(writer: &mut String, list: &[Node]) { + for n in list { + n.write_html(writer); + } +} + impl<'a> IntoIterator for &'a Node<'a> { type Item = &'a Node<'a>; type IntoIter = NodeIntoIterator<'a>; From 78bff775800642a94908d91341cf95770c169484 Mon Sep 17 00:00:00 2001 From: Alex Kazik Date: Wed, 11 Mar 2026 09:13:14 +0100 Subject: [PATCH 12/15] helper function --- src/dom/element.rs | 5 +++++ src/dom/html.rs | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/src/dom/element.rs b/src/dom/element.rs index a8e7a1b..42061f2 100644 --- a/src/dom/element.rs +++ b/src/dom/element.rs @@ -83,6 +83,11 @@ impl<'a> Children<'a> for Element<'a> { } impl Element<'_> { + #[inline] + pub fn has_class(&self, class: &str) -> bool { + self.classes.contains_key(class) + } + #[inline(always)] pub fn to_html(&self) -> String { let mut result = String::new(); diff --git a/src/dom/html.rs b/src/dom/html.rs index 2467ed9..b9e5ce8 100644 --- a/src/dom/html.rs +++ b/src/dom/html.rs @@ -46,6 +46,12 @@ impl<'a> PartialEq for &Attribute<'a> { } } +impl Borrow for Attribute<'_> { + fn borrow(&self) -> &str { + self.as_str() + } +} + #[derive(Default, Serialize, PartialEq, Eq, IntoOwned, ToBorrowed, ToOwned)] #[serde(transparent)] pub struct Text<'a>(pub Cow<'a, str>); From 60df8943454d231914213a492f74a16a34797f5e Mon Sep 17 00:00:00 2001 From: Alex Kazik Date: Sun, 8 Mar 2026 18:53:20 +0100 Subject: [PATCH 13/15] html en/decode --- Cargo.lock | 16 ++++++++++++++++ Cargo.toml | 1 + src/dom/html.rs | 9 +++++++++ 3 files changed, 26 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index d6ac08f..31c1224 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -657,12 +657,22 @@ dependencies = [ "libc", ] +[[package]] +name = "html-escape" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d1ad449764d627e22bfd7cd5e8868264fc9236e07c752972b4080cd351cb476" +dependencies = [ + "utf8-width", +] + [[package]] name = "html_parser" version = "0.7.0" dependencies = [ "clap 4.5.60", "criterion", + "html-escape", "indoc", "insta", "ownable", @@ -1838,6 +1848,12 @@ dependencies = [ "serde", ] +[[package]] +name = "utf8-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1292c0d970b54115d14f2492fe0170adf21d68a1de108eebc51c1df4f346a091" + [[package]] name = "utf8_iter" version = "1.0.4" diff --git a/Cargo.toml b/Cargo.toml index 6dfc5f4..b03d4d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ pest_derive = "2.5.7" thiserror = "1.0.40" serde = { version = "1.0.159", features = ["derive"] } serde_json = "1.0.95" +html-escape = "0.2" ownable = "1.0.0" [features] diff --git a/src/dom/html.rs b/src/dom/html.rs index b9e5ce8..fce037f 100644 --- a/src/dom/html.rs +++ b/src/dom/html.rs @@ -1,3 +1,4 @@ +use html_escape::{decode_html_entities, encode_text}; use ownable::{IntoOwned, ToBorrowed, ToOwned}; use serde::Serialize; use std::borrow::{Borrow, Cow}; @@ -60,6 +61,14 @@ impl<'a> Text<'a> { pub fn as_str(&self) -> &str { self.0.borrow() } + + pub fn decode(&self) -> Cow<'_, str> { + decode_html_entities(self.as_str()) + } + + pub fn encode(s: &'a str) -> Self { + Self(encode_text(s)) + } } impl Debug for Text<'_> { From a4fefb4b2cea82e0dd12212752233cb12d6c8cd2 Mon Sep 17 00:00:00 2001 From: Alex Kazik Date: Sun, 8 Mar 2026 20:11:52 +0100 Subject: [PATCH 14/15] to text --- src/dom/element.rs | 36 +++++++++++++++++++++++++++++++++++- src/dom/node.rs | 23 +++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/dom/element.rs b/src/dom/element.rs index 42061f2..e71080f 100644 --- a/src/dom/element.rs +++ b/src/dom/element.rs @@ -3,7 +3,8 @@ use super::node::{Node, write_html_list}; use super::span::SourceSpan; use crate::dom::vecmap::VecMap; use crate::dom::walk::Children; -use crate::{Attribute, VecSet}; +use crate::{Attribute, Text, VecSet}; +use html_escape::decode_html_entities; use ownable::{IntoOwned, ToBorrowed, ToOwned}; use serde::Serialize; use std::borrow::Cow; @@ -141,4 +142,37 @@ impl Element<'_> { writer.push('>'); } } + + /// Strip all tags. + /// + /// Note that html entities are not removed, only tags. + /// Use [`Self::to_text`], or [`Text::decode`] on the result. + pub fn strip_tags(&self) -> Text<'_> { + if let &[Node::Text(t)] = &self.children.as_slice() { + t.to_borrowed() + } else { + let mut result = String::new(); + self.write_strip_tags(&mut result); + Text(result.into()) + } + } + + fn write_strip_tags(&self, writer: &mut String) { + for c in &self.children { + match c { + Node::Text(t) => writer.push_str(t), + Node::Element(e) => e.write_strip_tags(writer), + Node::Comment(_) => (), + } + } + } + + /// Strip tags and decode html-entities. + pub fn to_text(&self) -> Cow<'_, str> { + let t = self.strip_tags().0; + match decode_html_entities(&t) { + Cow::Borrowed(_) => t, // it's only returned as borrowed if the whole input is passed though + Cow::Owned(o) => Cow::Owned(o), + } + } } diff --git a/src/dom/node.rs b/src/dom/node.rs index 01a1145..6b80425 100644 --- a/src/dom/node.rs +++ b/src/dom/node.rs @@ -1,9 +1,11 @@ use super::element::Element; use crate::Text; use crate::dom::walk::Children; +use html_escape::decode_html_entities; use ownable::{IntoOwned, ToBorrowed, ToOwned}; use serde::Serialize; use std::array; +use std::borrow::Cow; #[derive(Debug, Serialize, PartialEq, IntoOwned, ToBorrowed, ToOwned)] #[serde(untagged)] @@ -84,6 +86,27 @@ impl Node<'_> { } } } + + /// Strip all tags. + /// + /// Note that html entities are not removed, only tags. + /// Use [`Self::to_text`], or [`Text::decode`] on the result. + pub fn strip_tags(&self) -> Text<'_> { + match self { + Node::Text(t) => t.to_borrowed(), + Node::Element(e) => e.strip_tags(), + Node::Comment(_) => "".into(), + } + } + + /// Strip tags and decode html-entities. + pub fn to_text(&self) -> Cow<'_, str> { + let t = self.strip_tags().0; + match decode_html_entities(&t) { + Cow::Borrowed(_) => t, // it's only returned as borrowed if the whole input is passed though + Cow::Owned(o) => Cow::Owned(o), + } + } } pub(crate) fn write_html_list(writer: &mut String, list: &[Node]) { From 2f42778e11c7715f9cb3863765aa57f07865510f Mon Sep 17 00:00:00 2001 From: Alex Kazik Date: Wed, 11 Mar 2026 15:25:10 +0100 Subject: [PATCH 15/15] disable serde and debug by default serde and debug is not helpful except for tests --- Cargo.toml | 5 +- examples/simple_parser/main.rs | 113 ++++++++++++++++++--------------- src/dom/element.rs | 21 +++--- src/dom/html.rs | 11 ++-- src/dom/mod.rs | 22 +++++-- src/dom/node.rs | 7 +- src/dom/span.rs | 6 +- src/dom/vecmap.rs | 2 + src/dom/vecset.rs | 2 + src/error.rs | 1 + tests/bin.rs | 10 ++- tests/comments.rs | 1 + tests/document.rs | 1 + tests/document_empty.rs | 1 + tests/document_fragment.rs | 1 + tests/element.rs | 1 + tests/element_attributes.rs | 1 + tests/output.rs | 1 + tests/source_span.rs | 1 + tests/svg.rs | 1 + tests/text.rs | 1 + 21 files changed, 132 insertions(+), 78 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b03d4d1..0462e4f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,14 +14,15 @@ readme = "README.md" pest = "2.5.7" pest_derive = "2.5.7" thiserror = "1.0.40" -serde = { version = "1.0.159", features = ["derive"] } -serde_json = "1.0.95" +serde = { version = "1.0.159", features = ["derive"], optional = true } +serde_json = { version = "1.0.95", optional = true } html-escape = "0.2" ownable = "1.0.0" [features] default = ["source-span"] source-span = [] +test = ["dep:serde", "dep:serde_json"] [dev-dependencies] indoc = "2.0.1" diff --git a/examples/simple_parser/main.rs b/examples/simple_parser/main.rs index 11f5b90..94bc49a 100644 --- a/examples/simple_parser/main.rs +++ b/examples/simple_parser/main.rs @@ -1,61 +1,74 @@ -use clap::Parser; -use html_parser::{Dom, Result}; -use std::{ - fs::File, - io::{self, Read}, - path::PathBuf, -}; - -#[derive(Debug, Parser)] -/// A simple and general purpose html/xhtml parser. -struct Opt { - #[arg(short, long)] - /// Pretty-print the output. - pretty_print: bool, - - #[arg(short, long)] - /// Debug the parser, this will print errors to the console. - debug: bool, - - /// Path to the file, or stdin (piped content). - /// - /// This argument can either be a path to the html-file that you would like to parse or the - /// result of stdin. Note: Content over stdin needs to be finite, for now, as it is collected - /// into a string and then processed by the parser. - input: Option, +fn main() -> html_parser::Result<()> { + #[cfg(feature = "test")] + { + real::main() + } + + #[cfg(not(feature = "test"))] + panic!("this example requires the `test` feature to be enabled"); } -fn main() -> Result<()> { - let opt = Opt::parse(); +#[cfg(feature = "test")] +mod real { + use clap::Parser; + use html_parser::{Dom, Result}; + use std::{ + fs::File, + io::{self, Read}, + path::PathBuf, + }; - let mut content = String::with_capacity(100_000); + #[derive(Debug, Parser)] + /// A simple and general purpose html/xhtml parser. + struct Opt { + #[arg(short, long)] + /// Pretty-print the output. + pretty_print: bool, - // If input is provided then use that as a path - if let Some(path) = opt.input { - let mut file = File::open(path)?; - file.read_to_string(&mut content)?; + #[arg(short, long)] + /// Debug the parser, this will print errors to the console. + debug: bool, - // Else read from stdin, this enables piping - // ex: `cat index.html | html_parser` - } else { - let stdin = io::stdin(); - let mut handle = stdin.lock(); - handle.read_to_string(&mut content)?; - }; + /// Path to the file, or stdin (piped content). + /// + /// This argument can either be a path to the html-file that you would like to parse or the + /// result of stdin. Note: Content over stdin needs to be finite, for now, as it is collected + /// into a string and then processed by the parser. + input: Option, + } + + pub(super) fn main() -> Result<()> { + let opt = Opt::parse(); + + let mut content = String::with_capacity(100_000); - let dom = Dom::parse(&content)?; + // If input is provided then use that as a path + if let Some(path) = opt.input { + let mut file = File::open(path)?; + file.read_to_string(&mut content)?; - if opt.debug { - for error in &dom.errors { - println!("# {}", error); + // Else read from stdin, this enables piping + // ex: `cat index.html | html_parser` + } else { + let stdin = io::stdin(); + let mut handle = stdin.lock(); + handle.read_to_string(&mut content)?; + }; + + let dom = Dom::parse(&content)?; + + if opt.debug { + for error in &dom.errors { + println!("# {}", error); + } } - } - if opt.pretty_print { - println!("{}", dom.to_json_pretty()?); - } else { - println!("{}", dom.to_json()?); - } + if opt.pretty_print { + println!("{}", dom.to_json_pretty()?); + } else { + println!("{}", dom.to_json()?); + } - Ok(()) + Ok(()) + } } diff --git a/src/dom/element.rs b/src/dom/element.rs index e71080f..b74b596 100644 --- a/src/dom/element.rs +++ b/src/dom/element.rs @@ -6,13 +6,15 @@ use crate::dom::walk::Children; use crate::{Attribute, Text, VecSet}; use html_escape::decode_html_entities; use ownable::{IntoOwned, ToBorrowed, ToOwned}; +#[cfg(feature = "test")] use serde::Serialize; use std::borrow::Cow; use std::default::Default; /// Normal: `
` or Void: ``and `` -#[derive(Debug, Clone, Serialize, PartialEq)] -#[serde(rename_all = "camelCase")] +#[derive(Clone, PartialEq)] +#[cfg_attr(feature = "test", derive(Debug, Serialize))] +#[cfg_attr(feature = "test", serde(rename_all = "camelCase"))] // TODO: Align with: https://html.spec.whatwg.org/multipage/syntax.html#elements-2 pub enum ElementVariant { /// A normal element can have children, ex:
. @@ -24,11 +26,12 @@ pub enum ElementVariant { pub type Attributes<'a> = VecMap, Option>>; /// Most of the parsed html nodes are elements, except for text -#[derive(Debug, Serialize, PartialEq, IntoOwned, ToBorrowed, ToOwned)] -#[serde(rename_all = "camelCase")] +#[derive(PartialEq, IntoOwned, ToBorrowed, ToOwned)] +#[cfg_attr(feature = "test", derive(Debug, Serialize))] +#[cfg_attr(feature = "test", serde(rename_all = "camelCase"))] pub struct Element<'a> { /// The id of the element - #[serde(skip_serializing_if = "Option::is_none")] + #[cfg_attr(feature = "test", serde(skip_serializing_if = "Option::is_none"))] pub id: Option>, /// The name / tag of the element @@ -39,20 +42,20 @@ pub struct Element<'a> { pub variant: ElementVariant, /// All of the elements attributes, except id and class - #[serde(skip_serializing_if = "VecMap::is_empty")] + #[cfg_attr(feature = "test", serde(skip_serializing_if = "VecMap::is_empty"))] pub attributes: Attributes<'a>, /// All of the elements classes - #[serde(skip_serializing_if = "VecSet::is_empty")] + #[cfg_attr(feature = "test", serde(skip_serializing_if = "VecSet::is_empty"))] pub classes: VecSet>, /// All of the elements child nodes - #[serde(skip_serializing_if = "Vec::is_empty")] + #[cfg_attr(feature = "test", serde(skip_serializing_if = "Vec::is_empty"))] pub children: Vec>, #[cfg(feature = "source-span")] /// Span of the element in the parsed source - #[serde(skip)] + #[cfg_attr(feature = "test", serde(skip))] pub source_span: SourceSpan<'a>, } diff --git a/src/dom/html.rs b/src/dom/html.rs index fce037f..55af55a 100644 --- a/src/dom/html.rs +++ b/src/dom/html.rs @@ -1,12 +1,14 @@ use html_escape::{decode_html_entities, encode_text}; use ownable::{IntoOwned, ToBorrowed, ToOwned}; +#[cfg(feature = "test")] use serde::Serialize; use std::borrow::{Borrow, Cow}; use std::fmt::{Debug, Formatter}; use std::ops::Deref; -#[derive(Default, Debug, Serialize, PartialEq, Eq, IntoOwned, ToBorrowed, ToOwned)] -#[serde(transparent)] +#[derive(Default, PartialEq, Eq, IntoOwned, ToBorrowed, ToOwned)] +#[cfg_attr(feature = "test", derive(Debug, Serialize))] +#[cfg_attr(feature = "test", serde(transparent))] pub struct Attribute<'a>(pub Cow<'a, str>); impl<'a> Attribute<'a> { @@ -53,8 +55,9 @@ impl Borrow for Attribute<'_> { } } -#[derive(Default, Serialize, PartialEq, Eq, IntoOwned, ToBorrowed, ToOwned)] -#[serde(transparent)] +#[derive(Default, PartialEq, Eq, IntoOwned, ToBorrowed, ToOwned)] +#[cfg_attr(feature = "test", derive(Serialize))] +#[cfg_attr(feature = "test", serde(transparent))] pub struct Text<'a>(pub Cow<'a, str>); impl<'a> Text<'a> { diff --git a/src/dom/mod.rs b/src/dom/mod.rs index 1ad424c..9425865 100644 --- a/src/dom/mod.rs +++ b/src/dom/mod.rs @@ -1,5 +1,6 @@ use ownable::{IntoOwned, ToBorrowed, ToOwned}; use pest::{Parser, iterators::Pair, iterators::Pairs}; +#[cfg(feature = "test")] use serde::Serialize; use std::borrow::Cow; use std::default::Default; @@ -27,8 +28,9 @@ use element::{Element, ElementVariant}; use node::Node; /// Document, DocumentFragment or Empty -#[derive(Debug, Clone, Copy, PartialEq, Serialize)] -#[serde(rename_all = "camelCase")] +#[derive(Clone, Copy, PartialEq)] +#[cfg_attr(feature = "test", derive(Debug, Serialize))] +#[cfg_attr(feature = "test", serde(rename_all = "camelCase"))] pub enum DomVariant { /// This means that the parsed html had the representation of an html document. The doctype is optional but a document should only have one root node with the name of html. /// Example: @@ -53,19 +55,20 @@ pub enum DomVariant { } /// **The main struct** & the result of the parsed html -#[derive(Debug, Serialize, PartialEq, ToBorrowed, ToOwned, IntoOwned)] -#[serde(rename_all = "camelCase")] +#[derive(PartialEq, ToBorrowed, ToOwned, IntoOwned)] +#[cfg_attr(feature = "test", derive(Debug, Serialize))] +#[cfg_attr(feature = "test", serde(rename_all = "camelCase"))] pub struct Dom<'a> { /// The type of the tree that was parsed #[ownable(clone)] pub tree_type: DomVariant, /// All of the root children in the tree - #[serde(skip_serializing_if = "Vec::is_empty")] + #[cfg_attr(feature = "test", serde(skip_serializing_if = "Vec::is_empty"))] pub children: Vec>, /// A collection of all errors during parsing - #[serde(skip_serializing)] + #[cfg_attr(feature = "test", serde(skip_serializing))] #[ownable(clone)] pub errors: Vec, } @@ -89,10 +92,12 @@ impl<'a> Dom<'a> { Self::build_dom(pairs) } + #[cfg(feature = "test")] pub fn to_json(&self) -> Result { Ok(serde_json::to_string(self)?) } + #[cfg(feature = "test")] pub fn to_json_pretty(&self) -> Result { Ok(serde_json::to_string_pretty(self)?) } @@ -178,7 +183,10 @@ impl<'a> Dom<'a> { } else { // Anything else (i.e. Text() or Element() ) can't happen at the top level; // if we had seen one, we would have set the document type above - unreachable!("[build dom] empty document with an Element {:?}", node) + unreachable!( + "[build dom] empty document with an Element {:?}", + node.to_html() + ) } } } diff --git a/src/dom/node.rs b/src/dom/node.rs index 6b80425..1e3467a 100644 --- a/src/dom/node.rs +++ b/src/dom/node.rs @@ -3,12 +3,14 @@ use crate::Text; use crate::dom::walk::Children; use html_escape::decode_html_entities; use ownable::{IntoOwned, ToBorrowed, ToOwned}; +#[cfg(feature = "test")] use serde::Serialize; use std::array; use std::borrow::Cow; -#[derive(Debug, Serialize, PartialEq, IntoOwned, ToBorrowed, ToOwned)] -#[serde(untagged)] +#[derive(PartialEq, IntoOwned, ToBorrowed, ToOwned)] +#[cfg_attr(feature = "test", derive(Debug, Serialize))] +#[cfg_attr(feature = "test", serde(untagged))] pub enum Node<'a> { Text(Text<'a>), Element(Element<'a>), @@ -188,6 +190,7 @@ impl<'a> Iterator for NodeIntoIterator<'a> { } } +#[cfg(feature = "test")] #[cfg(test)] mod tests { use super::*; diff --git a/src/dom/span.rs b/src/dom/span.rs index 239cd55..d353c05 100644 --- a/src/dom/span.rs +++ b/src/dom/span.rs @@ -1,10 +1,12 @@ use ownable::{IntoOwned, ToBorrowed, ToOwned}; +#[cfg(feature = "test")] use serde::Serialize; use std::borrow::Cow; /// Span of the information in the parsed source. -#[derive(Debug, Default, Clone, Serialize, PartialEq, IntoOwned, ToBorrowed, ToOwned)] -#[serde(rename_all = "camelCase")] +#[derive(Default, Clone, PartialEq, IntoOwned, ToBorrowed, ToOwned)] +#[cfg_attr(feature = "test", derive(Debug, Serialize))] +#[cfg_attr(feature = "test", serde(rename_all = "camelCase"))] pub struct SourceSpan<'a> { pub text: Cow<'a, str>, pub start_line: usize, diff --git a/src/dom/vecmap.rs b/src/dom/vecmap.rs index 160c663..227d95e 100644 --- a/src/dom/vecmap.rs +++ b/src/dom/vecmap.rs @@ -1,4 +1,5 @@ use ownable::traits::{IntoOwned, ToBorrowed, ToOwned}; +#[cfg(feature = "test")] use serde::{Serialize, Serializer}; use std::borrow::Borrow; use std::fmt::{Debug, Formatter}; @@ -166,6 +167,7 @@ where } } +#[cfg(feature = "test")] impl Serialize for VecMap where K: Serialize, diff --git a/src/dom/vecset.rs b/src/dom/vecset.rs index 88cc692..7e99f37 100644 --- a/src/dom/vecset.rs +++ b/src/dom/vecset.rs @@ -1,5 +1,6 @@ use crate::VecMap; use ownable::traits::{IntoOwned, ToBorrowed, ToOwned}; +#[cfg(feature = "test")] use serde::{Serialize, Serializer}; use std::borrow::Borrow; use std::fmt::{Debug, Formatter}; @@ -109,6 +110,7 @@ impl IntoOwned for VecSet { } } +#[cfg(feature = "test")] impl Serialize for VecSet where K: Serialize, diff --git a/src/error.rs b/src/error.rs index 962c648..1a1ba4d 100644 --- a/src/error.rs +++ b/src/error.rs @@ -8,6 +8,7 @@ pub enum Error { Cli(String), #[error("{0}")] IO(#[from] std::io::Error), + #[cfg(feature = "test")] #[error("{0}")] Serde(#[from] serde_json::Error), } diff --git a/tests/bin.rs b/tests/bin.rs index 7d3ddc7..b2d7ff6 100644 --- a/tests/bin.rs +++ b/tests/bin.rs @@ -17,14 +17,20 @@ fn it_prints_out_processing_error() -> Result<()> { file.write_all(html.as_bytes())?; let output = Command::new("cargo") - .args(["run", "--example", "simple_parser", "--"]) + .args(["run", "--example", "simple_parser", "--all-features", "--"]) .arg("-d") .arg(file.path()) .output() .unwrap(); let stdout = String::from_utf8(output.stdout).unwrap(); + let stderr = String::from_utf8(output.stderr).unwrap(); - assert!(stdout.starts_with("# Failed to create element at rule: el_process_instruct")); + assert!( + stdout.starts_with("# Failed to create element at rule: el_process_instruct"), + "stdout:\n{}\nstderr:\n{}", + stdout, + stderr + ); Ok(()) } diff --git a/tests/comments.rs b/tests/comments.rs index a580155..16fd0b1 100644 --- a/tests/comments.rs +++ b/tests/comments.rs @@ -1,3 +1,4 @@ +#![cfg(feature = "test")] use html_parser::{Dom, Result}; use insta::assert_json_snapshot; diff --git a/tests/document.rs b/tests/document.rs index 1c9b5b6..7b6d0e2 100644 --- a/tests/document.rs +++ b/tests/document.rs @@ -1,3 +1,4 @@ +#![cfg(feature = "test")] use html_parser::{Dom, Result}; use indoc::indoc; use insta::assert_json_snapshot; diff --git a/tests/document_empty.rs b/tests/document_empty.rs index a08bbf5..8e83d50 100644 --- a/tests/document_empty.rs +++ b/tests/document_empty.rs @@ -1,3 +1,4 @@ +#![cfg(feature = "test")] use html_parser::{Dom, Result}; use insta::assert_json_snapshot; diff --git a/tests/document_fragment.rs b/tests/document_fragment.rs index ea4c150..ae54398 100644 --- a/tests/document_fragment.rs +++ b/tests/document_fragment.rs @@ -1,3 +1,4 @@ +#![cfg(feature = "test")] use html_parser::{Dom, Result}; use insta::assert_json_snapshot; diff --git a/tests/element.rs b/tests/element.rs index b75f242..fd83288 100644 --- a/tests/element.rs +++ b/tests/element.rs @@ -1,3 +1,4 @@ +#![cfg(feature = "test")] use html_parser::{Dom, Result}; use indoc::indoc; use insta::assert_json_snapshot; diff --git a/tests/element_attributes.rs b/tests/element_attributes.rs index 2994111..e30b2c1 100644 --- a/tests/element_attributes.rs +++ b/tests/element_attributes.rs @@ -1,3 +1,4 @@ +#![cfg(feature = "test")] use html_parser::{Dom, Result}; use insta::assert_json_snapshot; diff --git a/tests/output.rs b/tests/output.rs index 55bb42d..26d942b 100644 --- a/tests/output.rs +++ b/tests/output.rs @@ -1,3 +1,4 @@ +#![cfg(feature = "test")] use html_parser::{Dom, Result}; use indoc::indoc; use insta::assert_json_snapshot; diff --git a/tests/source_span.rs b/tests/source_span.rs index f720acf..ba475ea 100644 --- a/tests/source_span.rs +++ b/tests/source_span.rs @@ -1,3 +1,4 @@ +#![cfg(feature = "test")] use html_parser::{Dom, Result}; use indoc::indoc; use insta::assert_debug_snapshot; diff --git a/tests/svg.rs b/tests/svg.rs index 451a835..65e9a9e 100644 --- a/tests/svg.rs +++ b/tests/svg.rs @@ -1,3 +1,4 @@ +#![cfg(feature = "test")] use html_parser::{Dom, Result}; use indoc::indoc; use insta::assert_json_snapshot; diff --git a/tests/text.rs b/tests/text.rs index e1aeb60..b87535d 100644 --- a/tests/text.rs +++ b/tests/text.rs @@ -1,3 +1,4 @@ +#![cfg(feature = "test")] use html_parser::{Dom, Result}; use indoc::indoc; use insta::assert_json_snapshot;