From 955eab8d73a9216e56c1b510cf02305f6a68b22b Mon Sep 17 00:00:00 2001 From: QuartzLibrary <81446760+QuartzLibrary@users.noreply.github.com> Date: Sat, 11 Jul 2026 17:36:27 +0100 Subject: [PATCH 1/7] Split `cli` module --- src/{main.rs => cli.rs} | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) rename src/{main.rs => cli.rs} (99%) diff --git a/src/main.rs b/src/cli.rs similarity index 99% rename from src/main.rs rename to src/cli.rs index aa0883c..9d628ff 100644 --- a/src/main.rs +++ b/src/cli.rs @@ -152,8 +152,7 @@ impl Display for OutputFormat { } } -#[tokio::main] -async fn main() { +pub async fn main() { simple_logger::init_with_level(log::Level::Info).unwrap(); let command = Command::parse(); From ab665f0d7759581e717447ad60df17208f615e6d Mon Sep 17 00:00:00 2001 From: QuartzLibrary <81446760+QuartzLibrary@users.noreply.github.com> Date: Sat, 11 Jul 2026 17:38:31 +0100 Subject: [PATCH 2/7] Create main.rs --- src/main.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/main.rs diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..34aedbd --- /dev/null +++ b/src/main.rs @@ -0,0 +1,13 @@ +#[cfg(not(target_arch = "wasm32"))] +mod cli; + +#[cfg(not(target_arch = "wasm32"))] +#[tokio::main] +async fn main() { + cli::main().await; +} + +#[cfg(target_arch = "wasm32")] +fn main() { + println!("Not implemented for wasm32"); +} From c2dd0571f87fcb84584498eb8755316bd9dabdbf Mon Sep 17 00:00:00 2001 From: QuartzLibrary <81446760+QuartzLibrary@users.noreply.github.com> Date: Sat, 11 Jul 2026 16:29:54 +0100 Subject: [PATCH 3/7] Build on `wasm` --- .cargo/config.toml | 5 + Cargo.lock | 717 ++++++++++++++++++++++++++++++++++++++++++--- Cargo.toml | 14 +- src/api/mod.rs | 6 +- 4 files changed, 694 insertions(+), 48 deletions(-) create mode 100644 .cargo/config.toml diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..d530c0b --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,5 @@ +[profile.rust-analyzer] +inherits = "dev" + +[target.wasm32-unknown-unknown] +rustflags = ['--cfg', 'getrandom_backend="wasm_js"'] diff --git a/Cargo.lock b/Cargo.lock index 6e002b4..adff034 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -182,6 +182,15 @@ dependencies = [ "simd-abstraction", ] +[[package]] +name = "basic-toml" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a" +dependencies = [ + "serde", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -215,6 +224,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + [[package]] name = "bumpalo" version = "3.17.0" @@ -282,6 +300,27 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chacha20" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "rand_core 0.10.1", +] + +[[package]] +name = "charset" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1f927b07c74ba84c7e5fe4db2baeb3e996ab2688992e39ac68ce3220a677c7e" +dependencies = [ + "base64", + "encoding_rs", +] + [[package]] name = "chrono" version = "0.4.40" @@ -379,6 +418,15 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "content_disposition" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc14a88e1463ddd193906285abe5c360c7e8564e05ccc5d501755f7fbc9ca9c" +dependencies = [ + "charset", +] + [[package]] name = "convert_case" version = "0.4.0" @@ -419,6 +467,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" version = "1.4.2" @@ -512,6 +569,75 @@ dependencies = [ "syn 2.0.98", ] +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core 0.20.11", + "darling_macro 0.20.11", +] + +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core 0.23.0", + "darling_macro 0.23.0", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.98", +] + +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.98", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core 0.20.11", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core 0.23.0", + "quote", + "syn 2.0.98", +] + [[package]] name = "dashmap" version = "5.5.3" @@ -548,11 +674,11 @@ checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" [[package]] name = "deranged" -version = "0.3.11" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ - "powerfmt", + "serde_core", ] [[package]] @@ -610,6 +736,12 @@ dependencies = [ "dtoa", ] +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + [[package]] name = "either" version = "1.14.0" @@ -647,6 +779,17 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "erased-serde" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2add8a07dd6a8d93ff627029c51de145e12686fbc36ecb298ac22e74cf02dec" +dependencies = [ + "serde", + "serde_core", + "typeid", +] + [[package]] name = "errno" version = "0.3.10" @@ -767,43 +910,92 @@ dependencies = [ "new_debug_unreachable", ] +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", + "futures-sink", ] [[package]] name = "futures-core" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] [[package]] name = "futures-sink" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" [[package]] name = "futures-task" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-util" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ + "futures-channel", "futures-core", + "futures-io", + "futures-macro", + "futures-sink", "futures-task", + "memchr", "pin-project-lite", - "pin-utils", + "slab", ] [[package]] @@ -843,8 +1035,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", ] [[package]] @@ -854,11 +1048,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi 0.13.3+wasi-0.2.2", + "wasm-bindgen", "windows-targets 0.52.6", ] +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "rand_core 0.10.1", + "wasm-bindgen", +] + [[package]] name = "gif" version = "0.13.1" @@ -881,6 +1091,18 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +[[package]] +name = "gloo-timers" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + [[package]] name = "glowpub" version = "0.4.0" @@ -891,6 +1113,9 @@ dependencies = [ "cssparser 0.33.0", "epub-builder", "fontdb", + "getrandom 0.2.15", + "getrandom 0.3.1", + "getrandom 0.4.3", "glob", "html-escape", "html5ever", @@ -915,6 +1140,7 @@ dependencies = [ "tiny-skia", "tokio", "usvg", + "utile", "uuid", "xml5ever", ] @@ -931,7 +1157,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap", + "indexmap 2.7.1", "slab", "tokio", "tokio-util", @@ -974,6 +1200,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "html-escape" version = "0.2.13" @@ -1037,6 +1269,15 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a" +[[package]] +name = "humansize" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" +dependencies = [ + "libm", +] + [[package]] name = "hyper" version = "1.6.0" @@ -1250,6 +1491,12 @@ dependencies = [ "syn 2.0.98", ] +[[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.0.3" @@ -1310,6 +1557,17 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + [[package]] name = "indexmap" version = "2.7.1" @@ -1398,6 +1656,12 @@ version = "0.2.170" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + [[package]] name = "lightningcss" version = "1.0.0-alpha.63" @@ -1412,7 +1676,7 @@ dependencies = [ "dashmap", "data-encoding", "getrandom 0.2.15", - "indexmap", + "indexmap 2.7.1", "itertools", "lazy_static", "lightningcss-derive", @@ -1481,7 +1745,7 @@ dependencies = [ "memchr", "mime", "selectors", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -1549,6 +1813,22 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.8.5" @@ -1599,11 +1879,27 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "num-conv" -version = "0.1.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" [[package]] name = "num-traits" @@ -1872,16 +2168,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" [[package]] -name = "pin-project-lite" -version = "0.2.16" +name = "pin-project" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" +dependencies = [ + "pin-project-internal", +] [[package]] -name = "pin-utils" -version = "0.1.0" +name = "pin-project-internal" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pkg-config" @@ -1889,6 +2199,36 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +[[package]] +name = "plotly" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0746e9faf2b051db76470fd428cbc0db792db05346dedaae4a75b16d7be503b5" +dependencies = [ + "dyn-clone", + "erased-serde", + "once_cell", + "plotly_derive", + "rand 0.8.5", + "rinja", + "serde", + "serde_json", + "serde_repr", + "serde_with", +] + +[[package]] +name = "plotly_derive" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d683930282f098b9f524e2596e3e63483507ac499231c96127fcb166bc05d26" +dependencies = [ + "darling 0.20.11", + "proc-macro2", + "quote", + "syn 2.0.98", +] + [[package]] name = "png" version = "0.17.16" @@ -1973,6 +2313,12 @@ 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 = "radium" version = "0.7.0" @@ -2004,6 +2350,17 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" +dependencies = [ + "chacha20", + "getrandom 0.4.3", + "rand_core 0.10.1", +] + [[package]] name = "rand_chacha" version = "0.2.2" @@ -2042,6 +2399,12 @@ dependencies = [ "getrandom 0.2.15", ] +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + [[package]] name = "rand_hc" version = "0.2.0" @@ -2089,6 +2452,26 @@ dependencies = [ "bitflags 2.9.0", ] +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + [[package]] name = "regex" version = "1.11.1" @@ -2211,6 +2594,49 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rinja" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dc4940d00595430b3d7d5a01f6222b5e5b51395d1120bdb28d854bb8abb17a5" +dependencies = [ + "humansize", + "itoa 1.0.14", + "percent-encoding", + "rinja_derive", + "serde", + "serde_json", +] + +[[package]] +name = "rinja_derive" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d9ed0146aef6e2825f1b1515f074510549efba38d71f4554eec32eb36ba18b" +dependencies = [ + "basic-toml", + "memchr", + "mime", + "mime_guess", + "proc-macro2", + "quote", + "rinja_parser", + "rustc-hash", + "serde", + "syn 2.0.98", +] + +[[package]] +name = "rinja_parser" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f9a866e2e00a7a1fb27e46e9e324a6f7c0e7edc4543cae1d38f4e4a100c610" +dependencies = [ + "memchr", + "nom", + "serde", +] + [[package]] name = "rkyv" version = "0.7.45" @@ -2240,6 +2666,25 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "rmp" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ba8be72d372b2c9b35542551678538b562e7cf86c3315773cae48dfbfe7790c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "rmp-serde" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f81bee8c8ef9b577d1681a70ebbc962c232461e397b22c208c43c04b67a155" +dependencies = [ + "rmp", + "serde", +] + [[package]] name = "roxmltree" version = "0.19.0" @@ -2383,6 +2828,30 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -2446,18 +2915,28 @@ checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" [[package]] name = "serde" -version = "1.0.218" +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 = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.218" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -2466,14 +2945,26 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.139" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ "itoa 1.0.14", "memchr", - "ryu", "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_repr" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", ] [[package]] @@ -2488,6 +2979,38 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_with" +version = "3.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c" +dependencies = [ + "base64", + "bs58", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.7.1", + "schemars 0.9.0", + "schemars 1.2.1", + "serde_core", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660" +dependencies = [ + "darling 0.23.0", + "proc-macro2", + "quote", + "syn 2.0.98", +] + [[package]] name = "servo_arc" version = "0.1.1" @@ -2505,7 +3028,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "digest", ] @@ -2517,10 +3040,11 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.2" +version = "1.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" dependencies = [ + "errno", "libc", ] @@ -2807,7 +3331,16 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", ] [[package]] @@ -2821,34 +3354,44 @@ dependencies = [ "syn 2.0.98", ] +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + [[package]] name = "time" -version = "0.3.37" +version = "0.3.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" +checksum = "18dfaaeddcb932337b5e7866ee7d0ce9b76d2fd092997146f187ec09b4558a50" dependencies = [ "deranged", - "itoa 1.0.14", "libc", "num-conv", "num_threads", "powerfmt", - "serde", + "serde_core", "time-core", "time-macros", ] [[package]] name = "time-core" -version = "0.1.2" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "time-macros" -version = "0.2.19" +version = "0.2.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" +checksum = "c431b87111666e491a90baa837f914fb45cd5dc3c268591b0220ff5057f2085f" dependencies = [ "num-conv", "time-core", @@ -2962,6 +3505,7 @@ checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" dependencies = [ "bytes", "futures-core", + "futures-io", "futures-sink", "pin-project-lite", "tokio", @@ -3001,9 +3545,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", + "tracing-attributes", "tracing-core", ] +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + [[package]] name = "tracing-core" version = "0.1.33" @@ -3025,12 +3581,24 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + [[package]] name = "typenum" version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +[[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + [[package]] name = "unicode-bidi" version = "0.3.18" @@ -3117,6 +3685,7 @@ dependencies = [ "form_urlencoded", "idna", "percent-encoding", + "serde", ] [[package]] @@ -3176,6 +3745,40 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "utile" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "956aef103e8b83bc3b0a9732ca55c37b3e3de5d2a71c5640f86b2b0e162654ea" +dependencies = [ + "ahash 0.8.11", + "bytes", + "content_disposition", + "futures", + "getrandom 0.2.15", + "getrandom 0.3.1", + "getrandom 0.4.3", + "gloo-timers", + "log", + "nohash-hasher", + "pin-project", + "plotly", + "rand 0.10.2", + "regex", + "reqwest", + "rmp-serde", + "serde", + "serde_json", + "serde_with", + "thiserror 2.0.18", + "tokio", + "tokio-util", + "tracing", + "url", + "wasm-bindgen-futures", + "web-time", +] + [[package]] name = "uuid" version = "1.15.1" @@ -3183,6 +3786,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0f540e3240398cce6128b64ba83fdbdd86129c16a3aa1a3a252efd66eb3d587" dependencies = [ "getrandom 0.3.1", + "uuid-rng-internal", +] + +[[package]] +name = "uuid-rng-internal" +version = "1.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13ab16e069e7562ecbdfa2e7858097deff01e13284d62921b6db087d0d66dd76" +dependencies = [ + "getrandom 0.4.3", ] [[package]] @@ -3314,6 +3927,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "weezl" version = "0.1.8" @@ -3666,6 +4289,12 @@ dependencies = [ "flate2", ] +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + [[package]] name = "zune-core" version = "0.4.12" diff --git a/Cargo.toml b/Cargo.toml index dc51f98..9a46c76 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,12 +16,12 @@ mime = "0.3" rand = "0.8" regex = "1" sha2 = "0.10" +utile = "0.1" uuid = { version = "1", features = ["v4"] } reqwest = { version = "0.12", features = ["json"] } serde = { version = "1", features = ["derive"] } serde_json = "1" -tokio = { version = "1", features = ["full"] } fontdb = "0.16" resvg = "0.41" @@ -47,3 +47,15 @@ clap = { version = "4", features = ["derive"] } simple_logger = "4" slug = "0.1" rpassword = "7.3.1" + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +tokio = { version = "1", features = ["macros", "rt-multi-thread"] } + +[target.'cfg(target_arch = "wasm32")'.dependencies] +_getrandom4 = { version = "0.4", features = ["wasm_js"], package = "getrandom" } +_getrandom3 = { version = "0.3", features = ["wasm_js"], package = "getrandom" } +_getrandom2 = { version = "0.2", features = ["js"], package = "getrandom" } +uuid = { version = "1", features = ["rng-getrandom"] } + +[dev-dependencies] +tokio = { version = "1", features = ["full"] } diff --git a/src/api/mod.rs b/src/api/mod.rs index 66b663b..c29dfb6 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -76,7 +76,7 @@ impl Replies { Err(errors) => return Ok(Err(errors)), } - tokio::time::sleep(Duration::from_millis(100)).await; + utile::time::sleep(Duration::from_millis(100)).await; } Ok(Ok(replies)) @@ -137,7 +137,7 @@ impl BoardPosts { Err(errors) => return Ok(Err(errors)), } - tokio::time::sleep(Duration::from_millis(100)).await; + utile::time::sleep(Duration::from_millis(100)).await; } Ok(Ok(posts)) @@ -240,7 +240,7 @@ where if delay > Duration::from_secs(1) || i + 1 == retries { log::info!("Api error, retrying in {delay:?}. {e:?}"); } - tokio::time::sleep(delay).await; + utile::time::sleep(delay).await; } } } From b8999127cc6d680fef5619c055aca5be8b86e65f Mon Sep 17 00:00:00 2001 From: QuartzLibrary <81446760+QuartzLibrary@users.noreply.github.com> Date: Sat, 11 Jul 2026 17:39:37 +0100 Subject: [PATCH 4/7] Enable `wasm` CI --- .github/workflows/check.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 49648e7..6b4c8f4 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -31,15 +31,15 @@ jobs: target/ key: ${{ runner.os }}-cargo-clippy-${{ hashFiles('**/Cargo.lock') }} - # - name: Install WASM toolchain - # run: rustup target add wasm32-unknown-unknown + - name: Install WASM toolchain + run: rustup target add wasm32-unknown-unknown - name: Check project run: cargo clippy --workspace --all-targets --all-features -- -D warnings - # - name: Check project (WASM) - # # Note lack of --all-targets here because tests and examples are not wasm compatible - # run: cargo clippy --workspace --all-features --target wasm32-unknown-unknown -- -D warnings + - name: Check project (WASM) + # Note lack of --all-targets here because tests and examples are not wasm compatible + run: cargo clippy --workspace --all-features --target wasm32-unknown-unknown -- -D warnings test: runs-on: ubuntu-latest From cbd91f84a6e381f7205100913f5689dfc8a9e476 Mon Sep 17 00:00:00 2001 From: QuartzLibrary <81446760+QuartzLibrary@users.noreply.github.com> Date: Sat, 11 Jul 2026 18:04:12 +0100 Subject: [PATCH 5/7] Bypass cache on `wasm` --- src/cached.rs | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/cached.rs b/src/cached.rs index 1736171..cfe9c16 100644 --- a/src/cached.rs +++ b/src/cached.rs @@ -70,8 +70,7 @@ impl Replies { let response = Self::get_all(id).await?; - std::fs::create_dir_all(cache_path.parent().unwrap()).unwrap(); - write_if_changed(&cache_path, serde_json::to_vec_pretty(&response).unwrap()).unwrap(); + cache_store(&cache_path, serde_json::to_vec_pretty(&response).unwrap()); Ok(response) } @@ -101,8 +100,7 @@ impl BoardPosts { let response = Self::get_all(id).await?; - std::fs::create_dir_all(cache_path.parent().unwrap()).unwrap(); - write_if_changed(&cache_path, serde_json::to_vec_pretty(&response).unwrap()).unwrap(); + cache_store(&cache_path, serde_json::to_vec_pretty(&response).unwrap()); Ok(response) } @@ -139,8 +137,7 @@ impl Icon { let extension = mime_to_image_extension(&mime).ok_or(format!("Invalid mime: {mime}"))?; let cache_path = Self::cache_key(*id, &extension); - std::fs::create_dir_all(cache_path.parent().unwrap()).unwrap(); - write_if_changed(cache_path, &data).unwrap(); + cache_store(cache_path, &data); Ok((mime, data)) } @@ -247,8 +244,7 @@ pub async fn download_cached_image( let extension = mime_to_image_extension(&mime).ok_or(format!("Invalid mime: {mime}"))?; let cache_path = image_cache_key(&hash, &extension); - std::fs::create_dir_all(cache_path.parent().unwrap()).unwrap(); - write_if_changed(cache_path, &data).unwrap(); + cache_store(cache_path, &data); Ok((mime, data)) } @@ -271,8 +267,7 @@ where } let response = crate::api::get_glowfic(url).await?; - std::fs::create_dir_all(cache_path.parent().unwrap()).unwrap(); - write_if_changed(cache_path, serde_json::to_vec_pretty(&response).unwrap()).unwrap(); + cache_store(cache_path, serde_json::to_vec_pretty(&response).unwrap()); Ok(response) } @@ -323,6 +318,16 @@ fn read_image_file(path: PathBuf) -> Result<(Mime, Vec), Box> { } } +fn cache_store(path: impl AsRef, contents: impl AsRef<[u8]>) { + if cfg!(target_arch = "wasm32") { + // TODO: Implement cache storage for wasm32? + return; + } + let path = path.as_ref(); + std::fs::create_dir_all(path.parent().unwrap()).unwrap(); + write_if_changed(path, contents).unwrap(); +} + /// Avoids updating the last-modified date of the file. pub fn write_if_changed(path: impl AsRef, contents: impl AsRef<[u8]>) -> std::io::Result<()> { match std::fs::read(path.as_ref()) { From 3c41f0f6874d64adff65d672cddf467f76d52a24 Mon Sep 17 00:00:00 2001 From: QuartzLibrary <81446760+QuartzLibrary@users.noreply.github.com> Date: Sat, 11 Jul 2026 19:00:03 +0100 Subject: [PATCH 6/7] Allow generation without fetching --- src/generate/epub.rs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/generate/epub.rs b/src/generate/epub.rs index 2b2f860..84a2c4b 100644 --- a/src/generate/epub.rs +++ b/src/generate/epub.rs @@ -10,6 +10,7 @@ use uuid::Uuid; use crate::{ Board, Post, Reply, Thread, + intern_images::InternedImage, types::{Continuity, Section, User}, }; @@ -19,8 +20,15 @@ use super::{ impl Continuity { pub async fn to_epub(&self, options: Options) -> Result, Box> { - let mut images_to_intern = self.images_to_intern().await?; + let images_to_intern = self.images_to_intern().await?; + self.to_epub_from_images(options, images_to_intern) + } + pub fn to_epub_from_images( + &self, + options: Options, + mut images_to_intern: HashMap, + ) -> Result, Box> { if let Some(size) = options.resize_icons { images_to_intern = images_to_intern .into_iter() @@ -383,8 +391,15 @@ impl Thread { impl Thread { pub async fn to_epub(&self, options: Options) -> Result, Box> { - let mut images_to_intern = self.images_to_intern().await?; + let images_to_intern = self.images_to_intern().await?; + self.to_epub_from_images(options, images_to_intern) + } + pub fn to_epub_from_images( + &self, + options: Options, + mut images_to_intern: HashMap, + ) -> Result, Box> { if let Some(size) = options.resize_icons { images_to_intern = images_to_intern .into_iter() From d37d16885c64e7a2f3dc4b1b0dd868a8d48b1bd7 Mon Sep 17 00:00:00 2001 From: QuartzLibrary <81446760+QuartzLibrary@users.noreply.github.com> Date: Sat, 11 Jul 2026 19:21:08 +0100 Subject: [PATCH 7/7] Avoid potential panic --- src/cached.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/cached.rs b/src/cached.rs index cfe9c16..af01a94 100644 --- a/src/cached.rs +++ b/src/cached.rs @@ -275,8 +275,16 @@ where pub async fn download_image(url: &str) -> Result<(Mime, Vec), reqwest::Error> { let response = http_client().get(url).send().await?; - let content_type = response.headers().get(CONTENT_TYPE).unwrap(); - let mime = Mime::from_str(content_type.to_str().unwrap()).unwrap(); + let headers = response.headers(); + let mime = headers + .get(CONTENT_TYPE) + .and_then(|v| v.to_str().ok()) + .and_then(|s| Mime::from_str(s).ok()) + .or_else(|| { + utile::io::get_filename_from_headers(headers) + .and_then(|filename| extension_to_image_mime(filename.split('.').next_back()?)) + }) + .unwrap_or(mime::APPLICATION_OCTET_STREAM); let data = response.bytes().await?;