diff --git a/.gitignore b/.gitignore index 1b69360..ced31b5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # Added by cargo /target +target/ # Testing junk /my-site diff --git a/Cargo.lock b/Cargo.lock index 2a350a5..1e207f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -109,17 +109,11 @@ dependencies = [ "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 = "ar_archive_writer" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb93bbb63b9c227414f6eb3a0adfddca591a8ce1e9b60661bb08969b87e340b" +checksum = "4087686b4b0a3427190bae57a1d9a478dbb2d40c5dc1bd6e2b6d797913bdd348" dependencies = [ "object", ] @@ -132,28 +126,27 @@ checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "arrayvec" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +checksum = "f02882884d3e1bc524fb12c79f107f6ad0e1cfd498c536ffb494301740995dfe" [[package]] name = "atom_syndication" -version = "0.12.7" +version = "0.12.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f68d23e2cb4fd958c705b91a6b4c80ceeaf27a9e11651272a8389d5ce1a4a3" +checksum = "39d8b8ef99e33deb2a51504888222225797f48bace2f0cab01975746a39c05bb" dependencies = [ "chrono", "derive_builder", "diligent-date-parser", - "never", "quick-xml", ] [[package]] name = "autocfg" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "base64-simd" @@ -172,15 +165,15 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.11.1" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" [[package]] name = "bitvec" -version = "1.0.1" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +checksum = "ddcec3d12c579d40898fe0a9a358a803c23e9c52ca3c425707f81c9436211837" dependencies = [ "funty", "radium", @@ -213,19 +206,19 @@ dependencies = [ [[package]] name = "bstr" -version = "1.12.1" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" +checksum = "5cee35f73844aa3014bb606320a6c1f010249dbdf43342fe54b5a4f6a8ed4b79" dependencies = [ "memchr", - "serde", + "serde_core", ] [[package]] name = "bumpalo" -version = "3.20.2" +version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "bytecheck" @@ -257,15 +250,15 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.11.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593" [[package]] name = "cc" -version = "1.2.61" +version = "1.2.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" +checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" dependencies = [ "find-msvc-tools", "jobserver", @@ -281,9 +274,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "chrono" -version = "0.4.44" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" dependencies = [ "iana-time-zone", "js-sys", @@ -361,7 +354,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -512,7 +505,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", "crossterm_winapi", "document-features", "parking_lot", @@ -568,7 +561,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -592,7 +585,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -603,7 +596,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -652,7 +645,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -662,7 +655,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -713,13 +706,13 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -760,9 +753,9 @@ checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] name = "either" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" [[package]] name = "encoding_rs" @@ -773,6 +766,26 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "enumflags2" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" +dependencies = [ + "enumflags2_derive", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -899,7 +912,7 @@ checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -981,27 +994,24 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", "libc", "r-efi 6.0.0", - "wasip2", - "wasip3", ] [[package]] name = "getset" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf0fc11e47561d47397154977bc219f4cf809b2974facc3ccb3b89e2436f912" +checksum = "6cf442baaabe4213ce7d1239afc26c039180b6456da2cededa316ae2c8a77a77" dependencies = [ - "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1010,7 +1020,7 @@ version = "0.20.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b88256088d75a56f8ecfa070513a775dd9107f6530ef14919dac831af9cfe2b" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", "libc", "libgit2-sys", "log", @@ -1029,7 +1039,7 @@ dependencies = [ "bstr", "log", "regex-automata 0.4.14", - "regex-syntax 0.8.10", + "regex-syntax 0.8.11", ] [[package]] @@ -1038,7 +1048,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", "ignore", "walkdir", ] @@ -1100,9 +1110,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.17.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] name = "heck" @@ -1138,9 +1148,9 @@ dependencies = [ [[package]] name = "http" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" dependencies = [ "bytes", "itoa", @@ -1308,12 +1318,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - [[package]] name = "ident_case" version = "1.0.1" @@ -1343,9 +1347,9 @@ dependencies = [ [[package]] name = "ignore" -version = "0.4.25" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3d782a365a015e0f5c04902246139249abf769125006fbe7649e2ee88169b4a" +checksum = "b915661dd01db3f05050265b2477bcc6527b3792388e2749b41623cc592be67d" dependencies = [ "crossbeam-deque", "globset", @@ -1370,7 +1374,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.17.0", + "hashbrown 0.17.1", "serde", "serde_core", ] @@ -1386,11 +1390,11 @@ dependencies = [ [[package]] name = "inotify" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd5b3eaf1a28b758ac0faa5a4254e8ab2705605496f1b1f3fbbc3988ad73d199" +checksum = "533e68a5842e734946fe159fb03fc9bbbb254f590dd0d8ad321ae5ff7beca2c1" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", "inotify-sys", "libc", ] @@ -1410,7 +1414,7 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fddf93031af70e75410a2511ec04d49e758ed2f26dad3404a934e0fb45cc12a" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", "chrono", "crossterm 0.25.0", "dyn-clone", @@ -1492,21 +1496,20 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.97" +version = "0.3.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1840c94c045fbcf8ba2812c95db44499f7c64910a912551aaaa541decebcacf" +checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" dependencies = [ "cfg-if", "futures-util", - "once_cell", "wasm-bindgen", ] [[package]] name = "kqueue" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac30106d7dce88daf4a3fcb4879ea939476d5074a9b7ddd0fb97fa4bed5596a" +checksum = "273c0752728918e0ac4976f2b275b6fefb9ecd400585dec929419f3844cd87b5" dependencies = [ "kqueue-sys", "libc", @@ -1514,25 +1517,30 @@ dependencies = [ [[package]] name = "kqueue-sys" -version = "1.1.0" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7b65860415f949f23fa882e669f2dbd4a0f0eeb1acdd56790b30494afd7da2f" +checksum = "07293a4e297ac234359b510362495713f75ea345d5307140414f20c69ffeb087" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", "libc", ] [[package]] -name = "lazy_static" -version = "1.5.0" +name = "landlock" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +checksum = "635839550ae8b90d9fd2571460a6645dc0aec070225956ca7a2831ed31d2795d" +dependencies = [ + "enumflags2", + "libc", + "thiserror", +] [[package]] -name = "leb128fmt" -version = "0.1.0" +name = "lazy_static" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" @@ -1542,9 +1550,9 @@ checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libgit2-sys" -version = "0.18.3+1.9.2" +version = "0.18.5+1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9b3acc4b91781bb0b3386669d325163746af5f6e4f73e6d2d630e09a35f3487" +checksum = "005d6ae6eac1912906073e069f7db60b1fa98e052a68227824afe3e3a1c59ca2" dependencies = [ "cc", "libc", @@ -1554,6 +1562,16 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "libloading" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60" +dependencies = [ + "cfg-if", + "windows-link", +] + [[package]] name = "libm" version = "0.2.16" @@ -1562,14 +1580,14 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" +checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", "libc", "plain", - "redox_syscall 0.7.5", + "redox_syscall 0.8.1", ] [[package]] @@ -1588,9 +1606,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.28" +version = "1.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc3a226e576f50782b3305c5ccf458698f92798987f551c6a02efe8276721e22" +checksum = "85bc9657773828b90eeb625adff10eeac83cc21bbfd8e23a03eaa8a33c9e28d9" dependencies = [ "cc", "libc", @@ -1605,7 +1623,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb6314c2f0590ac93c86099b98bb7ba8abcf759bfd89604ffca906472bb54937" dependencies = [ "ahash 0.8.12", - "bitflags 2.11.1", + "bitflags 2.13.0", "const-str", "cssparser", "cssparser-color", @@ -1658,9 +1676,9 @@ checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" [[package]] name = "local-ip-address" -version = "0.6.12" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7b0187df4e614e42405b49511b82ff7a1774fbd9a816060ee465067847cac22" +checksum = "aa08fb2b1ec3ea84575e94b489d06d4ce0cbf052d12acd515838f50e3c3d63e3" dependencies = [ "libc", "neli", @@ -1678,9 +1696,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.29" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" [[package]] name = "matchers" @@ -1699,9 +1717,9 @@ checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "memchr" -version = "2.8.0" +version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" [[package]] name = "mime" @@ -1785,9 +1803,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" dependencies = [ "libc", "log", @@ -1818,7 +1836,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1827,7 +1845,7 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22f9786d56d972959e1408b6a93be6af13b9c1392036c5c1fafa08a1b0c6ee87" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", "byteorder", "derive_builder", "getset", @@ -1847,15 +1865,9 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.117", + "syn 2.0.118", ] -[[package]] -name = "never" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c96aba5aa877601bb3f6dd6a63a969e1f82e60646e81e71b14496995e9853c91" - [[package]] name = "newline-converter" version = "0.3.0" @@ -1882,6 +1894,9 @@ dependencies = [ "hyper", "indoc", "inquire", + "landlock", + "libc", + "libloading", "lightningcss", "local-ip-address", "mime_guess", @@ -1915,19 +1930,28 @@ dependencies = [ "whoami", ] +[[package]] +name = "norgolith-plugin-sdk" +version = "0.1.0" +dependencies = [ + "libc", + "serde", + "serde_json", +] + [[package]] name = "notify" version = "8.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", "fsevent-sys", "inotify", "kqueue", "libc", "log", - "mio 1.2.0", + "mio 1.2.1", "notify-types", "walkdir", "windows-sys 0.60.2", @@ -1952,7 +1976,7 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", ] [[package]] @@ -2007,9 +2031,9 @@ checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "open" -version = "5.3.4" +version = "5.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3bab717c29a857abf75fcef718d441ec7cb2725f937343c734740a985d37fd" +checksum = "2fbaa89d2ddc8473c78a3adf69eea8cffa28c483b8e02a971ef31527cd0fc92c" dependencies = [ "is-wsl", "libc", @@ -2024,18 +2048,18 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-src" -version = "300.6.0+3.6.2" +version = "300.6.1+3.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8e8cbfd3a4a8c8f089147fd7aaa33cf8c7450c4d09f8f80698a0cf093abeff4" +checksum = "46eb8fb9fb3b61ce1c0f8a026c4c1a0714d3a9e138e7fbde78753ce2babc3846" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.115" +version = "0.9.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "158fe5b292746440aa6e7a7e690e55aeb72d41505e2804c23c6973ad0e9c9781" +checksum = "b47e7e6bb2c38cd930d25a23b40fa52e068c10e85f3e03a7f5ba5aaca5713695" dependencies = [ "cc", "libc", @@ -2068,7 +2092,7 @@ version = "0.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54fd03f1ad26cb6b3ec1b7414fa78a3bd639e7dbb421b1a60513c96ce886a196" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", "cssparser", "log", "phf", @@ -2204,7 +2228,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2257,7 +2281,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2337,38 +2361,6 @@ dependencies = [ "termtree", ] -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn 2.0.117", -] - -[[package]] -name = "proc-macro-error-attr2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "proc-macro-error2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" -dependencies = [ - "proc-macro-error-attr2", - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "proc-macro2" version = "1.0.106" @@ -2410,9 +2402,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.37.5" +version = "0.39.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" +checksum = "cdcc8dd4e2f670d309a5f0e83fe36dfdc05af317008fea29144da1a2ac858e5e" dependencies = [ "encoding_rs", "memchr", @@ -2420,9 +2412,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.45" +version = "1.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" dependencies = [ "proc-macro2", ] @@ -2530,16 +2522,16 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", ] [[package]] name = "redox_syscall" -version = "0.7.5" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4666a1a60d8412eab19d94f6d13dcc9cea0a5ef4fdf6a5db306537413c661b1b" +checksum = "5b44b894f2a6e36457d665d1e08c3866add6ed5e70050c1b4ba8a8ddedb02ce7" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", ] [[package]] @@ -2555,14 +2547,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.12.3" +version = "1.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" dependencies = [ "aho-corasick 1.1.4", "memchr", "regex-automata 0.4.14", - "regex-syntax 0.8.10", + "regex-syntax 0.8.11", ] [[package]] @@ -2582,7 +2574,7 @@ checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick 1.1.4", "memchr", - "regex-syntax 0.8.10", + "regex-syntax 0.8.11", ] [[package]] @@ -2593,9 +2585,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" [[package]] name = "rend" @@ -2637,20 +2629,19 @@ dependencies = [ [[package]] name = "rss" -version = "2.0.12" +version = "2.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2107738f003660f0a91f56fd3e3bd3ab5d918b2ddaf1e1ec2136fb1c46f71bf" +checksum = "d0e38781082c53bdde56e6081698c06831f69a27eac2593ed6b6cb2511424ad5" dependencies = [ "atom_syndication", "derive_builder", - "never", "quick-xml", ] [[package]] name = "rust-norg" version = "0.1.0" -source = "git+https://github.com/nvim-neorg/rust-norg?branch=push-vznuopvolwup#02e2ab19ad6976d5916a8bc6e9cdc38302dc0051" +source = "git+https://github.com/nvim-neorg/rust-norg?branch=push-vznuopvolwup#9024eae647244a67e41bb1e387f7f7b8760d0eff" dependencies = [ "chumsky", "itertools 0.13.0", @@ -2677,7 +2668,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.13.0", "errno", "libc", "linux-raw-sys", @@ -2699,27 +2690,12 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "scc" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46e6f046b7fef48e2660c57ed794263155d713de679057f2d0c169bfc6e756cc" -dependencies = [ - "sdd", -] - [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "sdd" -version = "3.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "490dcfcbfef26be6800d11870ff2df8774fa6e86d047e3e8c8a76b25655e41ca" - [[package]] name = "seahash" version = "4.1.0" @@ -2772,14 +2748,14 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "serde_json" -version = "1.0.149" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ "indexmap", "itoa", @@ -2800,28 +2776,27 @@ dependencies = [ [[package]] name = "serial_test" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "911bd979bf1070a3f3aa7b691a3b3e9968f339ceeec89e08c280a8a22207a32f" +checksum = "699f4197115b8a7e7ff19c9a315a4bd6fffec26cc4626ef45ecaea389e081c6d" dependencies = [ "futures-executor", "futures-util", "log", "once_cell", "parking_lot", - "scc", "serial_test_derive", ] [[package]] name = "serial_test_derive" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a7d91949b85b0d2fb687445e448b40d322b6b3e4af6b44a29b21d9a5f33e6d9" +checksum = "94e153fc76e1c6a068703d6d29c508a0b15c061c4b7e43da59cc097bc342673c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2857,9 +2832,9 @@ dependencies = [ [[package]] name = "shlex" -version = "1.3.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" [[package]] name = "signal-hook" @@ -2931,15 +2906,15 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.15.1" +version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] name = "smawk" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" +checksum = "e8e2fb0f499abb4d162f2bedad68f5ef91a1682b5a03596ddb67efd37768d100" [[package]] name = "socket2" @@ -2953,9 +2928,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" dependencies = [ "libc", "windows-sys 0.61.2", @@ -3010,9 +2985,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.117" +version = "2.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" dependencies = [ "proc-macro2", "quote", @@ -3027,7 +3002,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3043,7 +3018,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.4.2", + "getrandom 0.4.3", "once_cell", "rustix", "windows-sys 0.61.2", @@ -3115,7 +3090,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3163,16 +3138,16 @@ dependencies = [ [[package]] name = "tokio" -version = "1.52.2" +version = "1.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "110a78583f19d5cdb2c5ccf321d1290344e71313c6c37d43520d386027d18386" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" dependencies = [ "bytes", "libc", - "mio 1.2.0", + "mio 1.2.1", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.3", + "socket2 0.6.4", "tokio-macros", "windows-sys 0.61.2", ] @@ -3185,7 +3160,7 @@ checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3292,7 +3267,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3349,7 +3324,7 @@ checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" dependencies = [ "bytes", "data-encoding", - "http 1.4.0", + "http 1.4.2", "httparse", "log", "rand 0.9.4", @@ -3360,9 +3335,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.20.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" [[package]] name = "ucd-trie" @@ -3390,9 +3365,9 @@ checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" [[package]] name = "unicode-segmentation" -version = "1.13.2" +version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" [[package]] name = "unicode-width" @@ -3406,12 +3381,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - [[package]] name = "unicode_categories" version = "0.1.1" @@ -3456,9 +3425,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.23.1" +version = "1.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" +checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53" dependencies = [ "js-sys", "wasm-bindgen", @@ -3515,20 +3484,11 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.3+wasi-0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" -dependencies = [ - "wit-bindgen 0.57.1", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +version = "1.0.4+wasi-0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" dependencies = [ - "wit-bindgen 0.51.0", + "wit-bindgen", ] [[package]] @@ -3539,9 +3499,9 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.120" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df52b6d9b87e0c74c9edfa1eb2d9bf85e5d63515474513aa50fa181b3c4f5db1" +checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" dependencies = [ "cfg-if", "once_cell", @@ -3552,9 +3512,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.120" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b1041f495fb322e64aca85f5756b2172e35cd459376e67f2a6c9dffcedb103" +checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3562,65 +3522,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.120" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dcd0ff20416988a18ac686d4d4d0f6aae9ebf08a389ff5d29012b05af2a1b41" +checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.120" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49757b3c82ebf16c57d69365a142940b384176c24df52a087fb748e2085359ea" +checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" dependencies = [ "unicode-ident", ] -[[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap", - "wasm-encoder", - "wasmparser", -] - -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags 2.11.1", - "hashbrown 0.15.5", - "indexmap", - "semver", -] - [[package]] name = "web-sys" -version = "0.3.97" +version = "0.3.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eadbac71025cd7b0834f20d1fe8472e8495821b4e9801eb0a60bd1f19827602" +checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141" dependencies = [ "js-sys", "wasm-bindgen", @@ -3689,7 +3615,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3700,7 +3626,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3967,100 +3893,12 @@ dependencies = [ "memchr", ] -[[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" version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" -[[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", - "prettyplease", - "syn 2.0.117", - "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 2.0.117", - "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.1", - "indexmap", - "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", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] - [[package]] name = "writeable" version = "0.6.3" @@ -4078,9 +3916,9 @@ dependencies = [ [[package]] name = "yoke" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" dependencies = [ "stable_deref_trait", "yoke-derive", @@ -4095,35 +3933,35 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.48" +version = "0.8.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.48" +version = "0.8.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "zerofrom" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" dependencies = [ "zerofrom-derive", ] @@ -4136,7 +3974,7 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", "synstructure", ] @@ -4170,7 +4008,7 @@ checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index c0428e9..98279b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,69 +1,3 @@ -[package] -name = "norgolith" -version = "0.4.0" -edition = "2021" -authors = ["NTBBloodbath String { - // Check if current commit is tagged with v* - let output = Command::new("git") - .args(["describe", "--exact-match", "--tags", "HEAD"]) - .output(); - - if let Ok(output) = output { - if output.status.success() { - let tag = String::from_utf8_lossy(&output.stdout).trim().to_string(); - return tag.strip_prefix('v').unwrap_or(&tag).to_string(); - } - } - - // Not at a tag, append commit hash - let cargo_version = env!("CARGO_PKG_VERSION"); - let hash = Command::new("git") - .args(["rev-parse", "--short", "HEAD"]) - .output() - .map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string()) - .unwrap_or_default(); - - if hash.is_empty() { - cargo_version.to_string() - } else { - format!("{}+{}", cargo_version, hash) - } -} diff --git a/core/Cargo.lock b/core/Cargo.lock new file mode 100644 index 0000000..1fbb9cb --- /dev/null +++ b/core/Cargo.lock @@ -0,0 +1,4224 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom 0.2.17", + "once_cell", + "version_check", +] + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "getrandom 0.3.4", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +dependencies = [ + "memchr", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "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 = "ar_archive_writer" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb93bbb63b9c227414f6eb3a0adfddca591a8ce1e9b60661bb08969b87e340b" +dependencies = [ + "object", +] + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "atom_syndication" +version = "0.12.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f68d23e2cb4fd958c705b91a6b4c80ceeaf27a9e11651272a8389d5ce1a4a3" +dependencies = [ + "chrono", + "derive_builder", + "diligent-date-parser", + "never", + "quick-xml", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "base64-simd" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "781dd20c3aff0bd194fe7d2a977dd92f21c173891f3a03b677359e5fa457e5d5" +dependencies = [ + "simd-abstraction", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "blake3" +version = "1.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aa83c34e62843d924f905e0f5c866eb1dd6545fc4d719e803d9ba6030371fce" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", + "cpufeatures 0.3.0", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bstr" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + +[[package]] +name = "bytecheck" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "cc" +version = "1.2.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chrono" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "chrono-tz" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93698b29de5e97ad0ae26447b344c482a7284c737d9ddc5f9e52b74a336671bb" +dependencies = [ + "chrono", + "chrono-tz-build", + "phf", +] + +[[package]] +name = "chrono-tz-build" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c088aee841df9c3041febbb73934cfc39708749bf96dc827e3359cd39ef11b1" +dependencies = [ + "parse-zoneinfo", + "phf", + "phf_codegen", +] + +[[package]] +name = "chumsky" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0d2bfadce76f963d776feff99db6dc33783829539258314776383b33e2a00f8" +dependencies = [ + "hashbrown 0.15.5", + "regex-automata 0.4.14", + "serde", + "stacker", + "unicode-ident", + "unicode-segmentation", +] + +[[package]] +name = "clap" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", + "terminal_size", +] + +[[package]] +name = "clap_derive" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + +[[package]] +name = "colored" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" +dependencies = [ + "lazy_static", + "windows-sys 0.59.0", +] + +[[package]] +name = "colored" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "comfy-table" +version = "7.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "958c5d6ecf1f214b4c2bbbbf6ab9523a864bd136dcf71a7e8904799acfe1ad47" +dependencies = [ + "crossterm 0.29.0", + "unicode-segmentation", + "unicode-width 0.2.2", +] + +[[package]] +name = "const-str" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21077772762a1002bb421c3af42ac1725fa56066bfc53d9a55bb79905df2aaf3" +dependencies = [ + "const-str-proc-macro", +] + +[[package]] +name = "const-str-proc-macro" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e1e0fdd2e5d3041e530e1b21158aeeef8b5d0e306bc5c1e3d6cf0930d10e25a" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "constant_time_eq" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +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 = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crossterm" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67" +dependencies = [ + "bitflags 1.3.2", + "crossterm_winapi", + "libc", + "mio 0.8.11", + "parking_lot", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" +dependencies = [ + "bitflags 2.11.1", + "crossterm_winapi", + "document-features", + "parking_lot", + "rustix", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "cssparser" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9be934d936a0fbed5bcdc01042b770de1398bf79d0e192f49fa7faea0e99281e" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa", + "phf", + "smallvec", +] + +[[package]] +name = "cssparser-color" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556c099a61d85989d7af52b692e35a8d68a57e7df8c6d07563dc0778b3960c9f" +dependencies = [ + "cssparser", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +dependencies = [ + "quote", + "syn 2.0.117", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[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.117", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "data-encoding" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" + +[[package]] +name = "data-url" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a30bfce702bcfa94e906ef82421f2c0e61c076ad76030c16ee5d2e9a32fe193" +dependencies = [ + "matches", +] + +[[package]] +name = "derive_builder" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" +dependencies = [ + "derive_builder_core", + "syn 2.0.117", +] + +[[package]] +name = "deunicode" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abd57806937c9cc163efc8ea3910e00a62e2aeb0b8119f1793a978088f8f6b04" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "diligent-date-parser" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ede7d79366f419921e2e2f67889c12125726692a313bffb474bd5f37a581e9" +dependencies = [ + "chrono", +] + +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.61.2", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + +[[package]] +name = "downcast" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" + +[[package]] +name = "dtoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590" + +[[package]] +name = "dtoa-short" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87" +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.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "enumflags2" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" +dependencies = [ + "enumflags2_derive", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + +[[package]] +name = "file-id" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1fc6a637b6dc58414714eddd9170ff187ecb0933d4c7024d1abbd23a3cc26e9" +dependencies = [ + "windows-sys 0.60.2", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fnv" +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 = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fragile" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8878864ba14bb86e818a412bfd6f18f9eabd4ec0f008a28e8f7eb61db532fcf9" +dependencies = [ + "futures-core", +] + +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +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-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-macro", + "futures-sink", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "fuzzy-matcher" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54614a3312934d066701a80f20f15fa3b56d67ac7722b39eea5b4c9dd1d66c94" +dependencies = [ + "thread_local", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "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.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi 5.3.0", + "wasip2", +] + +[[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 6.0.0", + "wasip2", + "wasip3", +] + +[[package]] +name = "getset" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf0fc11e47561d47397154977bc219f4cf809b2974facc3ccb3b89e2436f912" +dependencies = [ + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "git2" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b88256088d75a56f8ecfa070513a775dd9107f6530ef14919dac831af9cfe2b" +dependencies = [ + "bitflags 2.11.1", + "libc", + "libgit2-sys", + "log", + "openssl-probe", + "openssl-sys", + "url", +] + +[[package]] +name = "globset" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3" +dependencies = [ + "aho-corasick 1.1.4", + "bstr", + "log", + "regex-automata 0.4.14", + "regex-syntax 0.8.10", +] + +[[package]] +name = "globwalk" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" +dependencies = [ + "bitflags 2.11.1", + "ignore", + "walkdir", +] + +[[package]] +name = "h2" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.8", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash 0.8.12", + "bumpalo", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[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 = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[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 = "0.14.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http 0.2.12", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.5.10", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" + +[[package]] +name = "icu_properties" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" + +[[package]] +name = "icu_provider" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +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 = "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" +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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "ignore" +version = "0.4.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3d782a365a015e0f5c04902246139249abf769125006fbe7649e2ee88169b4a" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata 0.4.14", + "same-file", + "walkdir", + "winapi-util", +] + +[[package]] +name = "indenter" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.0", + "serde", + "serde_core", +] + +[[package]] +name = "indoc" +version = "2.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" +dependencies = [ + "rustversion", +] + +[[package]] +name = "inotify" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd5b3eaf1a28b758ac0faa5a4254e8ab2705605496f1b1f3fbbc3988ad73d199" +dependencies = [ + "bitflags 2.11.1", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + +[[package]] +name = "inquire" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fddf93031af70e75410a2511ec04d49e758ed2f26dad3404a934e0fb45cc12a" +dependencies = [ + "bitflags 2.11.1", + "chrono", + "crossterm 0.25.0", + "dyn-clone", + "fuzzy-matcher", + "fxhash", + "newline-converter", + "once_cell", + "unicode-segmentation", + "unicode-width 0.1.14", +] + +[[package]] +name = "is-docker" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" +dependencies = [ + "once_cell", +] + +[[package]] +name = "is-wsl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" +dependencies = [ + "is-docker", + "once_cell", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1840c94c045fbcf8ba2812c95db44499f7c64910a912551aaaa541decebcacf" +dependencies = [ + "cfg-if", + "futures-util", + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "kqueue" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac30106d7dce88daf4a3fcb4879ea939476d5074a9b7ddd0fb97fa4bed5596a" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7b65860415f949f23fa882e669f2dbd4a0f0eeb1acdd56790b30494afd7da2f" +dependencies = [ + "bitflags 2.11.1", + "libc", +] + +[[package]] +name = "landlock" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "635839550ae8b90d9fd2571460a6645dc0aec070225956ca7a2831ed31d2795d" +dependencies = [ + "enumflags2", + "libc", + "thiserror", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libgit2-sys" +version = "0.18.3+1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9b3acc4b91781bb0b3386669d325163746af5f6e4f73e6d2d630e09a35f3487" +dependencies = [ + "cc", + "libc", + "libssh2-sys", + "libz-sys", + "openssl-sys", + "pkg-config", +] + +[[package]] +name = "libloading" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "libredox" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" +dependencies = [ + "bitflags 2.11.1", + "libc", + "plain", + "redox_syscall 0.7.5", +] + +[[package]] +name = "libssh2-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "220e4f05ad4a218192533b300327f5150e809b54c4ec83b5a1d91833601811b9" +dependencies = [ + "cc", + "libc", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "libz-sys" +version = "1.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc3a226e576f50782b3305c5ccf458698f92798987f551c6a02efe8276721e22" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "lightningcss" +version = "1.0.0-alpha.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb6314c2f0590ac93c86099b98bb7ba8abcf759bfd89604ffca906472bb54937" +dependencies = [ + "ahash 0.8.12", + "bitflags 2.11.1", + "const-str", + "cssparser", + "cssparser-color", + "dashmap", + "data-encoding", + "getrandom 0.3.4", + "indexmap", + "itertools 0.10.5", + "lazy_static", + "lightningcss-derive", + "parcel_selectors", + "parcel_sourcemap", + "pastey", + "pathdiff", + "rayon", + "serde", + "serde-content", + "smallvec", +] + +[[package]] +name = "lightningcss-derive" +version = "1.0.0-alpha.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c12744d1279367caed41739ef094c325d53fb0ffcd4f9b84a368796f870252" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" + +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + +[[package]] +name = "local-ip-address" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7b0187df4e614e42405b49511b82ff7a1774fbd9a816060ee465067847cac22" +dependencies = [ + "libc", + "neli", + "windows-sys 0.61.2", +] + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "mime" +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 = "minify-html" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd4517942a8e7425c990b14977f86a63e4996eed7b15cfcca1540126ac5ff25" +dependencies = [ + "aho-corasick 0.7.20", + "lazy_static", + "lightningcss", + "memchr", + "minify-html-common", + "minify-js 0.5.6", + "once_cell", + "rustc-hash 1.1.0", +] + +[[package]] +name = "minify-html-common" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "697a6b40dffdc5de10c0cbd709dc2bc2039cea9dab8aaa636eb9a49d6b411780" +dependencies = [ + "aho-corasick 0.7.20", + "itertools 0.12.1", + "lazy_static", + "memchr", + "rustc-hash 1.1.0", + "serde", + "serde_json", +] + +[[package]] +name = "minify-js" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22d6c512a82abddbbc13b70609cb2beff01be2c7afff534d6e5e1c85e438fc8b" +dependencies = [ + "lazy_static", + "parse-js 0.17.0", +] + +[[package]] +name = "minify-js" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1fa5546ee8bd66024113e506cabe4230e76635a094c06ea2051b66021dda92e" +dependencies = [ + "aho-corasick 0.7.20", + "lazy_static", + "parse-js 0.20.1", +] + +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "mio" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "mockall" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39a6bfcc6c8c7eed5ee98b9c3e33adc726054389233e201c95dab2d41a3839d2" +dependencies = [ + "cfg-if", + "downcast", + "fragile", + "mockall_derive", + "predicates", + "predicates-tree", +] + +[[package]] +name = "mockall_derive" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ca3004c2efe9011bd4e461bd8256445052b9615405b4f7ea43fc8ca5c20898" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "neli" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22f9786d56d972959e1408b6a93be6af13b9c1392036c5c1fafa08a1b0c6ee87" +dependencies = [ + "bitflags 2.11.1", + "byteorder", + "derive_builder", + "getset", + "libc", + "log", + "neli-proc-macros", + "parking_lot", +] + +[[package]] +name = "neli-proc-macros" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d8d08c6e98f20a62417478ebf7be8e1425ec9acecc6f63e22da633f6b71609" +dependencies = [ + "either", + "proc-macro2", + "quote", + "serde", + "syn 2.0.117", +] + +[[package]] +name = "never" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96aba5aa877601bb3f6dd6a63a969e1f82e60646e81e71b14496995e9853c91" + +[[package]] +name = "newline-converter" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b6b097ecb1cbfed438542d16e84fd7ad9b0c76c8a65b7f9039212a3d14dc7f" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "norgolith" +version = "0.4.0" +dependencies = [ + "blake3", + "chrono", + "clap", + "colored 3.1.1", + "comfy-table", + "dirs", + "eyre", + "futures-util", + "git2", + "html-escape", + "hyper", + "indoc", + "inquire", + "landlock", + "libc", + "libloading", + "lightningcss", + "local-ip-address", + "mime_guess", + "minify-html", + "minify-js 0.6.0", + "mockall", + "notify", + "notify-debouncer-full", + "num_cpus", + "open", + "percent-encoding", + "rayon", + "regex", + "rss", + "rust-norg", + "semver", + "serde", + "serde_json", + "serial_test", + "spinoff", + "tempfile", + "tera", + "titlecase", + "tokio", + "tokio-stream", + "tokio-tungstenite", + "toml", + "tracing", + "tracing-subscriber", + "walkdir", + "whoami", +] + +[[package]] +name = "notify" +version = "8.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" +dependencies = [ + "bitflags 2.11.1", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "log", + "mio 1.2.0", + "notify-types", + "walkdir", + "windows-sys 0.60.2", +] + +[[package]] +name = "notify-debouncer-full" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2d88b1a7538054351c8258338df7c931a590513fb3745e8c15eb9ff4199b8d1" +dependencies = [ + "file-id", + "log", + "notify", + "notify-types", + "walkdir", +] + +[[package]] +name = "notify-types" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a" +dependencies = [ + "bitflags 2.11.1", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "open" +version = "5.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f3bab717c29a857abf75fcef718d441ec7cb2725f937343c734740a985d37fd" +dependencies = [ + "is-wsl", + "libc", + "pathdiff", +] + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-src" +version = "300.6.0+3.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8e8cbfd3a4a8c8f089147fd7aaa33cf8c7450c4d09f8f80698a0cf093abeff4" +dependencies = [ + "cc", +] + +[[package]] +name = "openssl-sys" +version = "0.9.115" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "158fe5b292746440aa6e7a7e690e55aeb72d41505e2804c23c6973ad0e9c9781" +dependencies = [ + "cc", + "libc", + "openssl-src", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "outref" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f222829ae9293e33a9f5e9f440c6760a3d450a64affe1846486b140db81c1f4" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "parcel_selectors" +version = "0.28.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54fd03f1ad26cb6b3ec1b7414fa78a3bd639e7dbb421b1a60513c96ce886a196" +dependencies = [ + "bitflags 2.11.1", + "cssparser", + "log", + "phf", + "phf_codegen", + "precomputed-hash", + "rustc-hash 2.1.2", + "smallvec", +] + +[[package]] +name = "parcel_sourcemap" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "485b74d7218068b2b7c0e3ff12fbc61ae11d57cb5d8224f525bd304c6be05bbb" +dependencies = [ + "base64-simd", + "data-url", + "rkyv", + "serde", + "serde_json", + "vlq", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.5.18", + "smallvec", + "windows-link", +] + +[[package]] +name = "parse-js" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ec3b11d443640ec35165ee8f6f0559f1c6f41878d70330fe9187012b5935f02" +dependencies = [ + "aho-corasick 0.7.20", + "bumpalo", + "hashbrown 0.13.2", + "lazy_static", + "memchr", +] + +[[package]] +name = "parse-js" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2742b5e32dcb5930447ed9f9e401a7dfd883867fc079c4fac44ae8ba3593710e" +dependencies = [ + "aho-corasick 0.7.20", + "bumpalo", + "hashbrown 0.13.2", + "lazy_static", + "memchr", +] + +[[package]] +name = "parse-zoneinfo" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24" +dependencies = [ + "regex", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pastey" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" + +[[package]] +name = "pathdiff" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pest" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" +dependencies = [ + "memchr", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11f486f1ea21e6c10ed15d5a7c77165d0ee443402f0780849d1768e7d9d6fe77" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8040c4647b13b210a963c1ed407c1ff4fdfa01c31d6d2a098218702e6664f94f" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "pest_meta" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220" +dependencies = [ + "pest", + "sha2", +] + +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_codegen" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared", + "rand 0.8.6", +] + +[[package]] +name = "phf_macros" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + +[[package]] +name = "potential_utf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +dependencies = [ + "zerovec", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "predicates" +version = "3.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ada8f2932f28a27ee7b70dd6c1c39ea0675c55a36879ab92f3a715eaa1e63cfe" +dependencies = [ + "anstyle", + "predicates-core", +] + +[[package]] +name = "predicates-core" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cad38746f3166b4031b1a0d39ad9f954dd291e7854fcc0eed52ee41a0b50d144" + +[[package]] +name = "predicates-tree" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0de1b847b39c8131db0467e9df1ff60e6d0562ab8e9a16e568ad0fdb372e2f2" +dependencies = [ + "predicates-core", + "termtree", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.117", +] + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "psm" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645dbe486e346d9b5de3ef16ede18c26e6c70ad97418f4874b8b1889d6e761ea" +dependencies = [ + "ar_archive_writer", + "cc", +] + +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "quick-xml" +version = "0.37.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" +dependencies = [ + "encoding_rs", + "memchr", +] + +[[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 = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "rayon" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.11.1", +] + +[[package]] +name = "redox_syscall" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4666a1a60d8412eab19d94f6d13dcc9cea0a5ef4fdf6a5db306537413c661b1b" +dependencies = [ + "bitflags 2.11.1", +] + +[[package]] +name = "redox_users" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick 1.1.4", + "memchr", + "regex-automata 0.4.14", + "regex-syntax 0.8.10", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick 1.1.4", + "memchr", + "regex-syntax 0.8.10", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "rend" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +dependencies = [ + "bytecheck", +] + +[[package]] +name = "rkyv" +version = "0.7.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2297bf9c81a3f0dc96bc9521370b88f054168c29826a75e89c55ff196e7ed6a1" +dependencies = [ + "bitvec", + "bytecheck", + "bytes", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84d7b42d4b8d06048d3ac8db0eb31bcb942cbeb709f0b5f2b2ebde398d3038f5" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rss" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2107738f003660f0a91f56fd3e3bd3ab5d918b2ddaf1e1ec2136fb1c46f71bf" +dependencies = [ + "atom_syndication", + "derive_builder", + "never", + "quick-xml", +] + +[[package]] +name = "rust-norg" +version = "0.1.0" +source = "git+https://github.com/nvim-neorg/rust-norg?branch=push-vznuopvolwup#02e2ab19ad6976d5916a8bc6e9cdc38302dc0051" +dependencies = [ + "chumsky", + "itertools 0.13.0", + "serde", + "textwrap", + "unicode_categories", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags 2.11.1", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scc" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46e6f046b7fef48e2660c57ed794263155d713de679057f2d0c169bfc6e756cc" +dependencies = [ + "sdd", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sdd" +version = "3.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490dcfcbfef26be6800d11870ff2df8774fa6e86d047e3e8c8a76b25655e41ca" + +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde-content" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3753ca04f350fa92d00b6146a3555e63c55388c9ef2e11e09bce2ff1c0b509c6" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "indexmap", + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "serial_test" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "911bd979bf1070a3f3aa7b691a3b3e9968f339ceeec89e08c280a8a22207a32f" +dependencies = [ + "futures-executor", + "futures-util", + "log", + "once_cell", + "parking_lot", + "scc", + "serial_test_derive", +] + +[[package]] +name = "serial_test_derive" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a7d91949b85b0d2fb687445e448b40d322b6b3e4af6b44a29b21d9a5f33e6d9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" +dependencies = [ + "libc", + "mio 0.8.11", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "simd-abstraction" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cadb29c57caadc51ff8346233b5cec1d240b68ce55cf1afc764818791876987" +dependencies = [ + "outref", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + +[[package]] +name = "siphasher" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "slug" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "882a80f72ee45de3cc9a5afeb2da0331d58df69e4e7d8eeb5d3c7784ae67e724" +dependencies = [ + "deunicode", + "wasm-bindgen", +] + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "smawk" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" + +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "socket2" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "spinoff" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20aa2ed67fbb202e7b716ff8bfc6571dd9301617767380197d701c31124e88f6" +dependencies = [ + "colored 2.2.0", + "once_cell", + "paste", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "stacker" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "640c8cdd92b6b12f5bcb1803ca3bbf5ab96e5e6b6b96b9ab77dabe9e880b3190" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "windows-sys 0.61.2", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.2", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "tera" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8004bca281f2d32df3bacd59bc67b312cb4c70cea46cbd79dbe8ac5ed206722" +dependencies = [ + "chrono", + "chrono-tz", + "globwalk", + "humansize", + "lazy_static", + "percent-encoding", + "pest", + "pest_derive", + "rand 0.8.6", + "regex", + "serde", + "serde_json", + "slug", + "unicode-segmentation", +] + +[[package]] +name = "terminal_size" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874" +dependencies = [ + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "termtree" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" + +[[package]] +name = "textwrap" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" +dependencies = [ + "smawk", + "unicode-linebreak", + "unicode-width 0.2.2", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[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.117", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "tinystr" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "titlecase" +version = "3.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb567088a91d59b492520c8149e2be5ce10d5deb2d9a383f3378df3259679d40" +dependencies = [ + "regex", +] + +[[package]] +name = "tokio" +version = "1.52.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "110a78583f19d5cdb2c5ccf321d1290344e71313c6c37d43520d386027d18386" +dependencies = [ + "bytes", + "libc", + "mio 1.2.0", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.6.3", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "tokio-stream" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "toml_write", + "winnow", +] + +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +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.117", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +dependencies = [ + "chrono", + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "tungstenite" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" +dependencies = [ + "bytes", + "data-encoding", + "http 1.4.0", + "httparse", + "log", + "rand 0.9.4", + "sha1", + "thiserror", + "utf-8", +] + +[[package]] +name = "typenum" +version = "1.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" + +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + +[[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + +[[package]] +name = "unicode-segmentation" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "vlq" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65dd7eed29412da847b0f78bcec0ac98588165988a8cfe41d4ea1d429f8ccfff" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.3+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +dependencies = [ + "wit-bindgen 0.57.1", +] + +[[package]] +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 = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen 0.51.0", +] + +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df52b6d9b87e0c74c9edfa1eb2d9bf85e5d63515474513aa50fa181b3c4f5db1" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b1041f495fb322e64aca85f5756b2172e35cd459376e67f2a6c9dffcedb103" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dcd0ff20416988a18ac686d4d4d0f6aae9ebf08a389ff5d29012b05af2a1b41" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.117", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49757b3c82ebf16c57d69365a142940b384176c24df52a087fb748e2085359ea" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags 2.11.1", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "web-sys" +version = "0.3.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eadbac71025cd7b0834f20d1fe8472e8495821b4e9801eb0a60bd1f19827602" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "whoami" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4a4db5077702ca3015d3d02d74974948aba2ad9e12ab7df718ee64ccd7e97d" +dependencies = [ + "libredox", + "wasite", + "web-sys", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[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.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "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.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "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-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" +dependencies = [ + "memchr", +] + +[[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" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[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", + "prettyplease", + "syn 2.0.117", + "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 2.0.117", + "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.1", + "indexmap", + "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", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "writeable" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "yoke" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zerofrom" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + +[[package]] +name = "zerotrie" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/core/Cargo.toml b/core/Cargo.toml new file mode 100644 index 0000000..b223259 --- /dev/null +++ b/core/Cargo.toml @@ -0,0 +1,75 @@ +[package] +name = "norgolith" +version = "0.4.0" +edition = "2021" +authors = ["NTBBloodbath String { + // Check if current commit is tagged with v* + let output = Command::new("git") + .args(["describe", "--exact-match", "--tags", "HEAD"]) + .output(); + + if let Ok(output) = output { + if output.status.success() { + let tag = String::from_utf8_lossy(&output.stdout).trim().to_string(); + return tag.strip_prefix('v').unwrap_or(&tag).to_string(); + } + } + + // Not at a tag, append commit hash + let cargo_version = env!("CARGO_PKG_VERSION"); + let hash = Command::new("git") + .args(["rev-parse", "--short", "HEAD"]) + .output() + .map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string()) + .unwrap_or_default(); + + if hash.is_empty() { + cargo_version.to_string() + } else { + format!("{}+{}", cargo_version, hash) + } +} + +fn compile_test_plugins() { + let plugins_dir = PathBuf::from("tests/plugins"); + if !plugins_dir.is_dir() { + return; + } + + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + let target_dir = out_dir.parent().unwrap().parent().unwrap(); // target/debug or target/release + + let cc = "cc"; + + let entries = match std::fs::read_dir(&plugins_dir) { + Ok(e) => e, + Err(_) => return, + }; + + for entry in entries.filter_map(|e| e.ok()) { + let path = entry.path(); + if path.extension().and_then(|s| s.to_str()) != Some("c") { + continue; + } + + let stem = path.file_stem().and_then(|s| s.to_str()).unwrap(); + let lib_name = if cfg!(target_os = "macos") { + format!("lib{}.dylib", stem) + } else { + format!("lib{}.so", stem) + }; + + let out_path = target_dir.join(&lib_name); + + let mut cmd = Command::new(cc); + cmd.arg("-shared") + .arg("-fPIC") + .arg("-o") + .arg(&out_path) + .arg(&path); + + #[cfg(target_os = "macos")] + cmd.arg("-undefined").arg("dynamic_lookup"); + + let status = cmd.status(); + if status.as_ref().map(|s| !s.success()).unwrap_or(true) { + // Non-fatal: test plugins are optional + println!( + "cargo:warning=failed to compile test plugin {}: {:?}", + stem, status + ); + } + } +} + +fn compile_rust_test_plugins() { + let plugins_dir = PathBuf::from("tests/plugins"); + if !plugins_dir.is_dir() { + return; + } + + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + let target_dir = out_dir.parent().unwrap().parent().unwrap(); + + let entries = match std::fs::read_dir(&plugins_dir) { + Ok(e) => e, + Err(_) => return, + }; + + for entry in entries.filter_map(|e| e.ok()) { + let path = entry.path(); + if !path.is_dir() || !path.join("Cargo.toml").exists() { + continue; + } + + let name = path.file_name().and_then(|s| s.to_str()).unwrap(); + + let mut cmd = Command::new("cargo"); + cmd.arg("build") + .arg("--release") + .arg("--manifest-path") + .arg(path.join("Cargo.toml")) + .arg("--target-dir") + .arg(target_dir); + + let status = cmd.status(); + if status.as_ref().map(|s| !s.success()).unwrap_or(true) { + println!( + "cargo:warning=failed to compile Rust test plugin {}: {:?}", + name, status + ); + } + } +} diff --git a/src/cache.rs b/core/src/cache.rs similarity index 100% rename from src/cache.rs rename to core/src/cache.rs diff --git a/src/cli.rs b/core/src/cli.rs similarity index 97% rename from src/cli.rs rename to core/src/cli.rs index 92a9c85..e3b8cca 100644 --- a/src/cli.rs +++ b/core/src/cli.rs @@ -132,6 +132,11 @@ enum Commands { #[arg(long = "no-minify")] _no_minify: bool, }, + /// Plugin management + Plugin { + #[command(subcommand)] + subcommand: cmd::PluginCommands, + }, /// Preview from build result Preview { #[arg(short = 'p', long, default_value_t = 3030, help = "Port to be used")] @@ -185,6 +190,7 @@ pub async fn start() -> Result<()> { minify: _, _no_minify, } => build_site(!_no_minify).await?, + Commands::Plugin { subcommand } => plugin_handle(&subcommand)?, Commands::New { kind, name, @@ -248,6 +254,10 @@ async fn theme_handle(subcommand: &cmd::ThemeCommands) -> Result<()> { cmd::theme(subcommand).await } +fn plugin_handle(subcommand: &cmd::PluginCommands) -> Result<()> { + cmd::plugin(subcommand) +} + /// Creates a new asset with the given kind and name. /// /// # Arguments diff --git a/src/cmd/build.rs b/core/src/cmd/build.rs similarity index 87% rename from src/cmd/build.rs rename to core/src/cmd/build.rs index d588147..1b399ae 100644 --- a/src/cmd/build.rs +++ b/core/src/cmd/build.rs @@ -17,7 +17,7 @@ fn href_root_re() -> &'static regex::Regex { RE.get_or_init(|| regex::Regex::new(r#"href="(/|/)"#).expect("valid regex")) } -use crate::{cache::BuildCache, config, fs, shared}; +use crate::{cache::BuildCache, config, fs, plugin, shared}; /// Represents the directory structure of a Norgolith site. /// @@ -188,7 +188,8 @@ fn generate_xml_feeds( /// * `paths` - Site directory paths /// * `site_config` - Site configuration /// * `minify` - Enable minification of output -#[instrument(level = "debug", skip(tera, paths, site_config, shared_context, cache))] +#[allow(clippy::too_many_arguments)] +#[instrument(level = "debug", skip(tera, paths, site_config, shared_context, cache, plugin_mgr))] fn build_contents( tera: &Tera, paths: &SitePaths, @@ -197,6 +198,7 @@ fn build_contents( shared_context: &Context, cache: &mut BuildCache, minify: bool, + plugin_mgr: &plugin::PluginManager, ) -> Result<(usize, BuildTimings)> { use rayon::prelude::*; @@ -225,6 +227,7 @@ fn build_contents( minify, shared_context, cache, + plugin_mgr, ) }) .collect(); @@ -274,7 +277,7 @@ type BuildResult = Result)>>; #[allow(clippy::too_many_arguments)] #[instrument( level = "debug", - skip(tera, paths, site_config, shared_context, cache) + skip(tera, paths, site_config, shared_context, cache, plugin_mgr) )] fn build_content_entry( path: &Path, @@ -284,6 +287,7 @@ fn build_content_entry( minify: bool, shared_context: &Context, cache: &BuildCache, + plugin_mgr: &plugin::PluginManager, ) -> BuildResult { let rel_path = path .strip_prefix(&paths.content) @@ -325,7 +329,7 @@ fn build_content_entry( let cached = cache.get(&cache_key, &content); // Load (parse_tree + HTML on miss, deserialization on hit) - let (metadata, cache_insert) = if let Some(cached) = cached { + let (mut metadata, cache_insert) = if let Some(cached) = cached { match serde_json::from_value::(cached.clone()) { Ok(md) => (md, None), Err(_) => { @@ -340,12 +344,74 @@ fn build_content_entry( (md, Some((cache_key, content.clone(), cache_val))) }; + // post_convert hook: modify HTML after Norg conversion, before Tera + if plugin_mgr.has_hook(plugin::HOOK_POST_CONVERT) { + if let Some(html) = metadata.get("raw").and_then(|v| v.as_str()) { + let input = serde_json::json!({ + "html": html, + "metadata": metadata, + "rel_path": rel_path.to_string_lossy(), + }) + .to_string(); + for p in plugin_mgr.plugins() { + if let Some(f) = p.hooks.post_convert { + match plugin_mgr.call_hook(p, f, &input) { + Ok(Some(new_html)) => { + if let toml::Value::Table(ref mut table) = metadata { + table.insert("raw".to_string(), toml::Value::String(new_html)); + } + } + Ok(None) => {} + Err(e) => { + error!( + "{} plugin '{}' on {}: {}", + "Plugin error:".red().bold(), + p.name.bold(), + rel_path.display(), + e + ); + } + } + } + } + } + } + // Determine output path let public_path = determine_public_path(&paths.public, rel_path)?; // Template render let mut rendered = shared::render_norg_page(tera, &metadata, shared_context)?; + // post_render hook: modify final HTML after Tera, before write + if plugin_mgr.has_hook(plugin::HOOK_POST_RENDER) { + let input = serde_json::json!({ + "html": rendered, + "metadata": metadata, + "rel_path": rel_path.to_string_lossy(), + }) + .to_string(); + for p in plugin_mgr.plugins() { + if let Some(f) = p.hooks.post_render { + match plugin_mgr.call_hook(p, f, &input) { + Ok(Some(new_html)) => { + rendered = new_html; + } + Ok(None) => {} + Err(e) => { + error!( + "{} plugin '{}' on {}: {}", + "Plugin error:".red().bold(), + p.name.bold(), + rel_path.display(), + e + ); + } + } + } + } + } + // Href rewrite let href_re = href_root_re(); rendered = href_re @@ -685,6 +751,7 @@ fn copy_assets(assets_dir: &Path, target_dir: &Path, minify: bool) -> Result Result<()> { let tera = shared::init_tera(paths.templates.to_str().unwrap(), &paths.theme_templates)?; timings.tera_ms = t.elapsed().as_millis(); + // Load plugins and apply sandbox + let t = Instant::now(); + let plugin_mgr = plugin::PluginManager::load(&root_dir); + let _ = plugin::sandbox::apply_landlock(&root_dir); + timings.plugins_ms = t.elapsed().as_millis(); + + println!(); + if !plugin_mgr.is_empty() { + println!( + " {} {} {} plugins", + "•".green(), + format!("{:<12}", "Plugins").bold(), + plugin_mgr.len() + ); + } + + // pre_build hook + if plugin_mgr.has_hook(plugin::HOOK_PRE_BUILD) { + let config_json = serde_json::to_string(&site_config) + .unwrap_or_default(); + for p in plugin_mgr.plugins() { + if let Some(f) = p.hooks.pre_build { + if let Err(e) = plugin_mgr.call_hook(p, f, &config_json) { + error!( + "{} plugin '{}': {}", + "Plugin error:".red().bold(), + p.name.bold(), + e + ); + } + } + } + } + // Prepare build directory let t = Instant::now(); prepare_build_directory(&paths.public)?; @@ -904,11 +1006,9 @@ pub fn build(minify: bool) -> Result<()> { let mut cache = BuildCache::open(&root_dir)?; timings.cache_open_ms = t.elapsed().as_millis(); - println!(); - // Build content let t = Instant::now(); - let (page_count, content_timings) = build_contents(&tera, &paths, &posts, &site_config, &shared_context, &mut cache, minify)?; + let (page_count, content_timings) = build_contents(&tera, &paths, &posts, &site_config, &shared_context, &mut cache, minify, &plugin_mgr)?; timings.content_ms = t.elapsed().as_millis(); timings.page_count = page_count; // Copy per-page sub-timings from the concurrent build @@ -966,6 +1066,24 @@ pub fn build(minify: bool) -> Result<()> { shared::get_elapsed_time(t).dimmed() ); + // post_build hook + if plugin_mgr.has_hook(plugin::HOOK_POST_BUILD) { + let config_json = serde_json::to_string(&site_config) + .unwrap_or_default(); + for p in plugin_mgr.plugins() { + if let Some(f) = p.hooks.post_build { + if let Err(e) = plugin_mgr.call_hook(p, f, &config_json) { + error!( + "{} plugin '{}': {}", + "Plugin error:".red().bold(), + p.name.bold(), + e + ); + } + } + } + } + println!(); let total_ms = build_start.elapsed().as_millis(); println!( @@ -974,6 +1092,22 @@ pub fn build(minify: bool) -> Result<()> { shared::get_elapsed_time(build_start) ); + // Print per-plugin timing if any hooks were called + let plugin_timings = plugin_mgr.hook_timings(); + if !plugin_timings.is_empty() { + println!(); + println!("{}", " Plugin hook timings:".dimmed()); + let mut sorted: Vec<_> = plugin_timings.iter().collect(); + sorted.sort_by(|a, b| b.1.cmp(a.1)); + for (name, duration) in sorted { + println!( + " {:<20} {:>6}ms", + name.dimmed(), + duration.as_millis() + ); + } + } + // Save cache let t = Instant::now(); if let Err(e) = cache.save() { diff --git a/src/cmd/dev.rs b/core/src/cmd/dev.rs similarity index 93% rename from src/cmd/dev.rs rename to core/src/cmd/dev.rs index e9f34b9..131dc7d 100644 --- a/src/cmd/dev.rs +++ b/core/src/cmd/dev.rs @@ -27,7 +27,7 @@ use tokio_tungstenite::accept_async; use tracing::{debug, error, info, instrument, warn}; use walkdir::WalkDir; -use crate::{config, fs, shared}; +use crate::{config, fs, plugin, shared}; /// Represents the directory structure of a Norgolith site. /// @@ -88,6 +88,7 @@ struct ServerState { posts: Arc>>, cache: Arc>, rendered_pages: Arc>>, + plugin_mgr: Arc, } impl ServerState { @@ -160,7 +161,7 @@ impl ServerState { let posts = self.posts.read().await.clone(); let cache = self.cache.read().await; - match render_all_pages(&tera, &self.paths, &config, &self.routes_url, &posts, &cache) { + match render_all_pages(&tera, &self.paths, &config, &self.routes_url, &posts, &cache, &self.plugin_mgr) { Ok(new_pages) => { let mut pages = self.rendered_pages.write().await; *pages = new_pages; @@ -1076,6 +1077,7 @@ async fn handle_server_request( /// Walks the content directory, renders each .norg file through the Tera template /// pipeline, and stores the HTML indexed by URL path. Also pre-renders category /// pages and XML feed templates. +#[allow(clippy::too_many_arguments)] fn render_all_pages( tera: &Tera, paths: &SitePaths, @@ -1083,6 +1085,7 @@ fn render_all_pages( routes_url: &str, posts: &[toml::Value], cache: &crate::cache::BuildCache, + plugin_mgr: &plugin::PluginManager, ) -> Result> { let mut pages = HashMap::new(); @@ -1117,7 +1120,7 @@ fn render_all_pages( // Full load with HTML conversion (reuse build_cache if available) let cache_key = rel_path.with_extension(""); - let metadata = if let Some(cached) = cache.get(&cache_key, &content) { + let mut metadata = if let Some(cached) = cache.get(&cache_key, &content) { serde_json::from_value(cached).unwrap_or_else(|_| { shared::load_metadata_from_content(&content, rel_path, routes_url) }) @@ -1125,8 +1128,70 @@ fn render_all_pages( shared::load_metadata_from_content(&content, rel_path, routes_url) }; + // post_convert hook: modify HTML after Norg conversion, before Tera + if plugin_mgr.has_hook(plugin::HOOK_POST_CONVERT) { + if let Some(html) = metadata.get("raw").and_then(|v| v.as_str()) { + let input = serde_json::json!({ + "html": html, + "metadata": metadata, + "rel_path": rel_path.to_string_lossy(), + }) + .to_string(); + for p in plugin_mgr.plugins() { + if let Some(f) = p.hooks.post_convert { + match plugin_mgr.call_hook(p, f, &input) { + Ok(Some(new_html)) => { + if let toml::Value::Table(ref mut table) = metadata { + table.insert("raw".to_string(), toml::Value::String(new_html)); + } + } + Ok(None) => {} + Err(e) => { + error!( + "{} plugin '{}' on {}: {}", + "Plugin error:".red().bold(), + p.name.bold(), + rel_path.display(), + e + ); + } + } + } + } + } + } + let mut body = shared::render_norg_page(tera, &metadata, &shared_context)?; + // post_render hook: modify final HTML after Tera, before URL rewrite + if plugin_mgr.has_hook(plugin::HOOK_POST_RENDER) { + let input = serde_json::json!({ + "html": body, + "metadata": metadata, + "rel_path": rel_path.to_string_lossy(), + }) + .to_string(); + for p in plugin_mgr.plugins() { + if let Some(f) = p.hooks.post_render { + match plugin_mgr.call_hook(p, f, &input) { + Ok(Some(new_html)) => { + body = new_html; + } + Ok(None) => {} + Err(e) => { + error!( + "{} plugin '{}' on {}: {}", + "Plugin error:".red().bold(), + p.name.bold(), + rel_path.display(), + e + ); + } + } + } + } + } + // Always use the proper URL to the development server for template links that refers // to the local URL, this is useful when running the server exposed to LAN network body = body.replace( @@ -1262,6 +1327,30 @@ async fn setup_server_state( // Open build cache for incremental renders let cache = crate::cache::BuildCache::open(&root_dir)?; + // Load plugins, apply sandbox, run pre_build hook + let plugin_mgr = plugin::PluginManager::load(&root_dir); + let _ = plugin::sandbox::apply_landlock(&root_dir); + if plugin_mgr.has_hook(plugin::HOOK_PRE_BUILD) { + let input = serde_json::json!({ + "site_config": site_config, + "pages_dir": paths.content, + "output_dir": root_dir.join("public"), + }) + .to_string(); + for p in plugin_mgr.plugins() { + if let Some(f) = p.hooks.pre_build { + if let Err(e) = plugin_mgr.call_hook(p, f, &input) { + error!( + "{} plugin '{}': {}", + "Plugin error:".red().bold(), + p.name.bold(), + e + ); + } + } + } + } + // Pre-render all pages into memory for instant serving let rendered_pages = render_all_pages( &tera, @@ -1270,6 +1359,7 @@ async fn setup_server_state( &routes_url, &posts, &cache, + &plugin_mgr, )?; let tera = Arc::new(RwLock::new(tera)); @@ -1284,6 +1374,7 @@ async fn setup_server_state( posts: Arc::new(RwLock::new(posts)), cache: Arc::new(RwLock::new(cache)), rendered_pages: Arc::new(RwLock::new(rendered_pages)), + plugin_mgr: Arc::new(plugin_mgr), })) } diff --git a/src/cmd/init.rs b/core/src/cmd/init.rs similarity index 99% rename from src/cmd/init.rs rename to core/src/cmd/init.rs index 25569cb..f1be092 100644 --- a/src/cmd/init.rs +++ b/core/src/cmd/init.rs @@ -148,7 +148,7 @@ async fn create_assets(root: &str) -> Result<()> { .await .map_err(|e| eyre!("Failed to write style.css: {}", e))?; - let norgolith_logo = include_str!("../../res/norgolith.svg"); + let norgolith_logo = include_str!("../../../res/norgolith.svg"); let logo_path = assets_dir.join("norgolith.svg"); debug!(logo_path = %logo_path.display(), "Writing norgolith.svg"); fs::write(&logo_path, norgolith_logo) diff --git a/src/cmd/mod.rs b/core/src/cmd/mod.rs similarity index 74% rename from src/cmd/mod.rs rename to core/src/cmd/mod.rs index 8be10cc..188948d 100644 --- a/src/cmd/mod.rs +++ b/core/src/cmd/mod.rs @@ -2,6 +2,7 @@ mod build; mod dev; mod init; mod new; +mod plugin; mod preview; mod theme; @@ -9,6 +10,8 @@ pub use build::build; pub use dev::dev; pub use init::init; pub use new::new; +pub use plugin::handle as plugin; +pub use plugin::PluginCommands; pub use preview::preview; pub use theme::handle as theme; pub use theme::ThemeCommands; diff --git a/src/cmd/new.rs b/core/src/cmd/new.rs similarity index 100% rename from src/cmd/new.rs rename to core/src/cmd/new.rs diff --git a/core/src/cmd/plugin.rs b/core/src/cmd/plugin.rs new file mode 100644 index 0000000..f6dbd79 --- /dev/null +++ b/core/src/cmd/plugin.rs @@ -0,0 +1,295 @@ +use std::path::{Path, PathBuf}; + +use clap::Subcommand; +use colored::Colorize; +use eyre::{bail, Result}; + +use crate::plugin::{self, PluginManifest, CORE_ABI_VERSION}; + +#[derive(Subcommand, Clone)] +pub enum PluginCommands { + /// List installed plugins and their status + List, + /// Scaffold a new plugin project + New { + /// Plugin name (used for directory and crate name) + name: String, + }, + /// Build and install a plugin from a local path + Install { + /// Path to the plugin directory (containing Cargo.toml) + path: PathBuf, + }, + /// Remove an installed plugin + Uninstall { + /// Plugin name to remove + name: String, + }, +} + +pub fn handle(subcommand: &PluginCommands) -> Result<()> { + match subcommand { + PluginCommands::List => list_plugins(), + PluginCommands::New { name } => new_plugin(name), + PluginCommands::Install { path } => install_plugin(path), + PluginCommands::Uninstall { name } => uninstall_plugin(name), + } +} + +fn list_plugins() -> Result<()> { + let cwd = std::env::current_dir()?; + let mgr = plugin::PluginManager::load(&cwd); + + if mgr.is_empty() { + println!("{}", "No plugins installed.".dimmed()); + println!( + "Install one with {} or scaffold a new one with {}", + "lith plugin install ".cyan(), + "lith plugin new ".cyan() + ); + return Ok(()); + } + + for p in mgr.plugins() { + let hooks = p.manifest.hooks.declared_hooks(); + let hooks_str = if hooks.is_empty() { + "none".dimmed().to_string() + } else { + hooks.join(", ") + }; + + let status = if hooks.is_empty() { + "no hooks".yellow().to_string() + } else { + "ok".green().to_string() + }; + + let fs = match p.manifest.capabilities.filesystem { + plugin::FilesystemAccess::None => "none".dimmed().to_string(), + plugin::FilesystemAccess::Read => "read".to_string(), + plugin::FilesystemAccess::Write => "write".to_string(), + plugin::FilesystemAccess::ReadWrite => "read-write".to_string(), + }; + let net = if p.manifest.capabilities.network { + "yes".to_string() + } else { + "no".dimmed().to_string() + }; + + println!("{}", p.name.bold()); + println!( + " version: {:<10} hooks: {}", + p.version, hooks_str + ); + println!( + " status: {:<19} priority: {}", + status, p.manifest.priority + ); + println!( + " abi: {:<10} norgolith: {}", + p.manifest.plugin.abi, p.manifest.plugin.norgolith + ); + println!( + " timeout: {:<10} fs: {} net: {}", + format!("{}s", p.manifest.timeout_ms / 1000), + fs, + net + ); + } + + println!("\n{}", format!("{} plugin(s) loaded", mgr.len()).bold()); + Ok(()) +} + +fn new_plugin(name: &str) -> Result<()> { + validate_plugin_name(name)?; + + let cwd = std::env::current_dir()?; + let plugins_dir = cwd.join("plugins").join(name); + + if plugins_dir.exists() { + bail!("Plugin '{}' already exists at {}", name, plugins_dir.display()); + } + + std::fs::create_dir_all(plugins_dir.join("src"))?; + + // NOTE: I still need to handle the case where the plugin requires a dev norgolith version, e.g. (>=0.4.0-COMMIT_HASH) + // For now, just use the current version of norgolith from Cargo.toml. I'll need to figure out if semver crate can + // handle this case, or if I need to implement a custom version comparison for dev versions. + const NORGOLITH_VERSION: &str = env!("CARGO_PKG_VERSION"); + + // plugin.toml + let manifest = format!( + r#"[plugin] +name = "{name}" +version = "0.1.0" +norgolith = ">={NORGOLITH_VERSION}" +abi = {CORE_ABI_VERSION} + +[hooks] +pre_build = false +post_convert = false +post_render = false +post_build = false + +[capabilities] +filesystem = "none" +network = false + +timeout_ms = 10000 +priority = 100 +"# + ); + std::fs::write(plugins_dir.join("plugin.toml"), manifest)?; + + // Cargo.toml + let cargo_toml = format!( + r#"[package] +name = "{name}" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +norgolith-plugin-sdk = "0.1" +"# + ); + std::fs::write(plugins_dir.join("Cargo.toml"), cargo_toml)?; + + // src/lib.rs + let lib_rs = format!( + r#"use norgolith_plugin_sdk::*; + +register_plugin!("{name}", "0.1.0") + .on_post_render(|ctx| {{ + Ok(Some(ctx.html)) + }}) + .register(); +"# + ); + std::fs::write(plugins_dir.join("src").join("lib.rs"), lib_rs)?; + + println!( + "Plugin '{}' created at {}", + name.bold(), + plugins_dir.display() + ); + println!("\nNext steps:"); + println!(" 1. cd plugins/{}", name); + println!(" 2. Implement your hooks in src/lib.rs"); + println!(" 3. Build with `cargo build`"); + println!(" 4. Test with `lith plugin install plugins/{}'", name); + + Ok(()) +} + +fn install_plugin(source_dir: &Path) -> Result<()> { + if !source_dir.is_dir() { + bail!("Not a directory: {}", source_dir.display()); + } + + let manifest_path = source_dir.join("plugin.toml"); + if !manifest_path.is_file() { + bail!( + "No plugin.toml found in {}", + source_dir.display() + ); + } + + let manifest = PluginManifest::load(&manifest_path)?; + validate_plugin_name(&manifest.plugin.name)?; + manifest.validate_abi()?; + manifest.validate_semver()?; + + // Build the plugin + println!("{}", "Building plugin...".dimmed()); + let status = std::process::Command::new("cargo") + .arg("build") + .arg("--release") + .current_dir(source_dir) + .status()?; + + if !status.success() { + bail!("cargo build failed"); + } + + // Find the built library + let target_dir = source_dir.join("target").join("release"); + let lib_name = plugin::library_filename(&manifest.plugin.name); + let lib_path = target_dir.join(&lib_name); + + if !lib_path.is_file() { + // Fallback: scan for any matching library + let ext = plugin::library_extension(); + let found = std::fs::read_dir(&target_dir) + .ok() + .and_then(|entries| { + entries + .filter_map(|e| e.ok()) + .find(|e| { + e.path() + .extension() + .and_then(|s| s.to_str()) + .map(|s| s == ext) + .unwrap_or(false) + }) + .map(|e| e.path()) + }); + + match found { + Some(path) => { + let cwd = std::env::current_dir()?; + let dest_dir = cwd.join("plugins").join(&manifest.plugin.name); + std::fs::create_dir_all(&dest_dir)?; + std::fs::copy(&path, dest_dir.join(path.file_name().unwrap()))?; + std::fs::copy(&manifest_path, dest_dir.join("plugin.toml"))?; + } + None => { + bail!( + "Built library not found in {}", + target_dir.display() + ); + } + } + } else { + let cwd = std::env::current_dir()?; + let dest_dir = cwd.join("plugins").join(&manifest.plugin.name); + std::fs::create_dir_all(&dest_dir)?; + std::fs::copy(&lib_path, dest_dir.join(&lib_name))?; + std::fs::copy(&manifest_path, dest_dir.join("plugin.toml"))?; + } + + println!( + "Plugin '{}' v{} installed", + manifest.plugin.name.bold(), + manifest.plugin.version + ); + Ok(()) +} + +fn validate_plugin_name(name: &str) -> Result<()> { + if name.is_empty() { + bail!("Plugin name cannot be empty"); + } + if name.contains('/') || name.contains('\\') || name.contains("..") || name.contains(':') { + bail!("Invalid plugin name: '{}' (no path separators or '..' allowed)", name); + } + Ok(()) +} + +fn uninstall_plugin(name: &str) -> Result<()> { + validate_plugin_name(name)?; + + let cwd = std::env::current_dir()?; + let plugin_dir = cwd.join("plugins").join(name); + + if !plugin_dir.is_dir() { + bail!("Plugin '{}' is not installed", name); + } + + std::fs::remove_dir_all(&plugin_dir)?; + println!("Plugin '{}' uninstalled", name.bold()); + Ok(()) +} diff --git a/src/cmd/preview.rs b/core/src/cmd/preview.rs similarity index 100% rename from src/cmd/preview.rs rename to core/src/cmd/preview.rs diff --git a/src/cmd/theme.rs b/core/src/cmd/theme.rs similarity index 100% rename from src/cmd/theme.rs rename to core/src/cmd/theme.rs diff --git a/src/config/mod.rs b/core/src/config/mod.rs similarity index 100% rename from src/config/mod.rs rename to core/src/config/mod.rs diff --git a/src/converter/html.rs b/core/src/converter/html.rs similarity index 100% rename from src/converter/html.rs rename to core/src/converter/html.rs diff --git a/src/converter/meta.rs b/core/src/converter/meta.rs similarity index 100% rename from src/converter/meta.rs rename to core/src/converter/meta.rs diff --git a/src/converter/mod.rs b/core/src/converter/mod.rs similarity index 100% rename from src/converter/mod.rs rename to core/src/converter/mod.rs diff --git a/src/fs.rs b/core/src/fs.rs similarity index 100% rename from src/fs.rs rename to core/src/fs.rs diff --git a/src/main.rs b/core/src/main.rs similarity index 98% rename from src/main.rs rename to core/src/main.rs index 2a96e38..48877f5 100644 --- a/src/main.rs +++ b/core/src/main.rs @@ -5,6 +5,7 @@ mod config; mod converter; mod fs; mod net; +mod plugin; mod schema; mod shared; mod tera_functions; diff --git a/src/net.rs b/core/src/net.rs similarity index 100% rename from src/net.rs rename to core/src/net.rs diff --git a/core/src/plugin/ffi.rs b/core/src/plugin/ffi.rs new file mode 100644 index 0000000..c60d38d --- /dev/null +++ b/core/src/plugin/ffi.rs @@ -0,0 +1,170 @@ +use std::ffi::{CStr, CString}; +use std::os::raw::c_char; +use std::panic::AssertUnwindSafe; +use std::sync::mpsc; +use std::time::Duration; + +use eyre::{bail, Result}; + +/// Wrapper to make raw pointer results Send-safe across threads +struct HookResult(Option<*mut c_char>); + +// SAFETY: we only send the pointer across threads, actual deref happens on the receiving side +unsafe impl Send for HookResult {} + +/// C ABI PluginInfo struct returned by `norgolith_plugin_init` +#[repr(C)] +pub struct PluginInfo { + /// Which version of plugin.h this .so was compiled against + pub abi_version: u32, + /// Human-readable plugin name for error messages + pub name: *const c_char, + /// Semantic version for debugging (not validation) + pub version: *const c_char, +} + +/// Function pointer type for plugin hooks +pub type PluginFn = extern "C" fn(*const c_char) -> *mut c_char; + +/// Function pointer type for freeing plugin-allocated strings +pub type FreeStringFn = extern "C" fn(*mut c_char); + +/// Call a plugin hook with catch_unwind + thread timeout +/// +/// Returns `Ok(None)` if the plugin returned NULL (no change) +/// Returns `Ok(Some(json))` if the plugin returned modified content +/// Returns `Err(msg)` on panic, timeout, or invalid output +/// +/// # Safety Note +/// The returned pointer is freed with `libc::free`. This assumes the plugin's global allocator is +/// compatible with libc malloc (true for the default system allocator). Plugins compiled with +/// jemalloc or mimalloc will cause UB. This is an acceptable trade-off for MVP; a future version +/// can add a `plugin_free` callback to let each plugin provide its own deallocator +pub fn call_hook_safe( + f: PluginFn, + input: &str, + timeout: Duration, +) -> Result> { + let c_input = CString::new(input) + .map_err(|e| eyre::eyre!("failed to create CString: {}", e))?; + + let (tx, rx) = mpsc::channel(); + + std::thread::spawn(move || { + let result = std::panic::catch_unwind(AssertUnwindSafe(|| { + HookResult(Some(f(c_input.as_ptr()))) + })); + let _ = tx.send(result); + }); + + match rx.recv_timeout(timeout) { + Ok(Ok(HookResult(ptr))) => { + let ptr = ptr.unwrap(); + if ptr.is_null() { + return Ok(None); + } + let result = unsafe { CStr::from_ptr(ptr) } + .to_string_lossy() + .into_owned(); + unsafe { libc::free(ptr as *mut libc::c_void) }; + Ok(Some(result)) + } + Ok(Err(panic)) => { + let msg = match panic.downcast_ref::<&str>() { + Some(s) => s.to_string(), + None => match panic.downcast_ref::() { + Some(s) => s.clone(), + None => "unknown panic".to_string(), + }, + }; + Err(eyre::eyre!("plugin panicked: {}", msg)) + } + Err(_timeout) => { + Err(eyre::eyre!( + "plugin hook timed out after {}ms", + timeout.as_millis() + )) + } + } +} + +/// Parse a hook response JSON and extract the HTML field +/// +/// Returns `Ok(None)` if html is null (no change) +/// Returns `Ok(Some(html))` if html is present +/// Returns `Err` on error status or invalid JSON +pub fn parse_hook_response(json: &str) -> Result> { + let val: serde_json::Value = serde_json::from_str(json) + .map_err(|e| eyre::eyre!("invalid JSON from plugin: {}", e))?; + + if let Some(status) = val.get("status").and_then(|v| v.as_str()) { + if status == "error" { + let msg = val + .get("message") + .and_then(|v| v.as_str()) + .unwrap_or("unknown error"); + bail!("plugin error: {}", msg); + } + } + + match val.get("html").and_then(|v| v.as_str()) { + Some(html) => Ok(Some(html.to_string())), + None => Ok(None), + } +} + +/// Parse a hook response for pre_build/post_build (status only) +pub fn parse_status_response(json: &str) -> Result<()> { + let val: serde_json::Value = serde_json::from_str(json) + .map_err(|e| eyre::eyre!("invalid JSON from plugin: {}", e))?; + + if let Some(status) = val.get("status").and_then(|v| v.as_str()) { + if status == "error" { + let msg = val + .get("message") + .and_then(|v| v.as_str()) + .unwrap_or("unknown error"); + bail!("plugin error: {}", msg); + } + } + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_parse_hook_response_with_html() { + let json = r#"{"html": "

Hello

"}"#; + assert_eq!( + parse_hook_response(json).unwrap(), + Some("

Hello

".to_string()) + ); + } + + #[test] + fn test_parse_hook_response_null_html() { + let json = r#"{"html": null}"#; + assert_eq!(parse_hook_response(json).unwrap(), None); + } + + #[test] + fn test_parse_hook_response_error() { + let json = r#"{"status": "error", "message": "something broke"}"#; + assert!(parse_hook_response(json).is_err()); + } + + #[test] + fn test_parse_status_response_ok() { + assert!(parse_status_response(r#"{"status": "ok"}"#).is_ok()); + } + + #[test] + fn test_parse_status_response_error() { + assert!(parse_status_response( + r#"{"status": "error", "message": "init failed"}"# + ) + .is_err()); + } +} diff --git a/core/src/plugin/manifest.rs b/core/src/plugin/manifest.rs new file mode 100644 index 0000000..ea05cec --- /dev/null +++ b/core/src/plugin/manifest.rs @@ -0,0 +1,163 @@ +use std::path::Path; + +use eyre::{bail, eyre, Result}; +use serde::Deserialize; + +/// Current ABI version that this norgolith core provides +pub const CORE_ABI_VERSION: u32 = 1; + +/// Default hook timeout in milliseconds +const DEFAULT_TIMEOUT_MS: u64 = 10_000; + +/// Parsed representation of a `plugin.toml` manifest +#[derive(Debug, Clone, Deserialize)] +pub struct PluginManifest { + pub plugin: PluginMetadata, + pub hooks: HookConfig, + #[serde(default)] + pub capabilities: Capabilities, + #[serde(default = "default_timeout_ms")] + pub timeout_ms: u64, + /// Execution priority (lower runs first, default 100) + #[serde(default = "default_priority")] + pub priority: u32, +} + +fn default_priority() -> u32 { + 100 +} + +#[derive(Debug, Clone, Deserialize)] +pub struct PluginMetadata { + /// Name of the plugin (e.g. "my-plugin") + pub name: String, + /// Version of the plugin (e.g. "0.1.0") + pub version: String, + /// Semver requirement for norgolith compatibility (e.g. ">=0.4.0") + pub norgolith: String, + /// ABI version this plugin was compiled against + pub abi: u32, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct HookConfig { + #[serde(default)] + pub pre_build: bool, + #[serde(default)] + pub post_convert: bool, + #[serde(default)] + pub post_render: bool, + #[serde(default)] + pub post_build: bool, +} + +#[derive(Debug, Clone, Deserialize, Default)] +pub struct Capabilities { + #[serde(default)] + pub filesystem: FilesystemAccess, + #[serde(default)] + pub network: bool, +} + +#[derive(Debug, Clone, Deserialize, Default, PartialEq, Eq)] +#[serde(rename_all = "kebab-case")] +pub enum FilesystemAccess { + #[default] + None, + Read, + Write, + #[serde(rename = "read-write")] + ReadWrite, +} + +fn default_timeout_ms() -> u64 { + DEFAULT_TIMEOUT_MS +} + +impl HookConfig { + /// Returns a bitmask of declared hooks + /// Bits: PRE_BUILD=1, POST_CONVERT=2, POST_RENDER=4, POST_BUILD=8 + pub fn to_mask(&self) -> u32 { + let mut mask = 0u32; + if self.pre_build { + mask |= HOOK_PRE_BUILD; + } + if self.post_convert { + mask |= HOOK_POST_CONVERT; + } + if self.post_render { + mask |= HOOK_POST_RENDER; + } + if self.post_build { + mask |= HOOK_POST_BUILD; + } + mask + } + + pub fn declared_hooks(&self) -> Vec<&'static str> { + let mut hooks = Vec::new(); + if self.pre_build { + hooks.push("pre_build"); + } + if self.post_convert { + hooks.push("post_convert"); + } + if self.post_render { + hooks.push("post_render"); + } + if self.post_build { + hooks.push("post_build"); + } + hooks + } +} + +/// Plugin needs to configure itself before any file processing +pub const HOOK_PRE_BUILD: u32 = 1; +/// Plugin needs to modify HTML after Norg->HTML conversion but before Tera templating +pub const HOOK_POST_CONVERT: u32 = 2; +/// Plugin needs to modify final HTML after Tera layout is applied +pub const HOOK_POST_RENDER: u32 = 4; +/// Plugin needs to generate additional output files after all pages are rendered +pub const HOOK_POST_BUILD: u32 = 8; + +impl PluginManifest { + /// Parse a `plugin.toml` file at the given path + pub fn load(path: &Path) -> Result { + let content = std::fs::read_to_string(path) + .map_err(|e| eyre!("Failed to read {}: {}", path.display(), e))?; + let manifest: PluginManifest = toml::from_str(&content) + .map_err(|e| eyre!("Failed to parse {}: {}", path.display(), e))?; + Ok(manifest) + } + + /// Validate ABI compatibility. Returns Ok(()) if compatible + pub fn validate_abi(&self) -> Result<()> { + if self.plugin.abi != CORE_ABI_VERSION { + bail!( + "ABI mismatch: plugin '{}' requires abi={}, core provides abi={}", + self.plugin.name, + self.plugin.abi, + CORE_ABI_VERSION + ); + } + Ok(()) + } + + /// Validate semver compatibility with the running norgolith version + pub fn validate_semver(&self) -> Result<()> { + let req = semver::VersionReq::parse(&self.plugin.norgolith) + .map_err(|e| eyre!("Invalid semver requirement '{}': {}", self.plugin.norgolith, e))?; + let current = semver::Version::parse(env!("CARGO_PKG_VERSION")) + .map_err(|e| eyre!("Invalid core version: {}", e))?; + if !req.matches(¤t) { + bail!( + "Version mismatch: plugin '{}' requires norgolith {}, installed is {}", + self.plugin.name, + self.plugin.norgolith, + current + ); + } + Ok(()) + } +} diff --git a/core/src/plugin/mod.rs b/core/src/plugin/mod.rs new file mode 100644 index 0000000..65d54bf --- /dev/null +++ b/core/src/plugin/mod.rs @@ -0,0 +1,538 @@ +#![allow(dead_code, unused_imports)] + +pub mod ffi; +pub mod manifest; +pub mod sandbox; + +use std::collections::HashMap; +use std::path::{Path, PathBuf}; +use std::sync::{Arc, Mutex}; +use std::time::{Duration, Instant}; + +use eyre::Result; + +pub use ffi::{FreeStringFn, PluginFn, PluginInfo}; +pub use manifest::{ + Capabilities, FilesystemAccess, HookConfig, PluginManifest, CORE_ABI_VERSION, + HOOK_POST_BUILD, HOOK_POST_CONVERT, HOOK_POST_RENDER, HOOK_PRE_BUILD, +}; + +use tracing::{info, warn}; + +/// Hooks a plugin can implement. Each is an optional C ABI function pointer +pub struct PluginHooks { + pub pre_build: Option, + pub post_convert: Option, + pub post_render: Option, + pub post_build: Option, +} + +/// A loaded plugin instance +pub struct PluginInstance { + pub name: String, + pub version: String, + /// Keeps the `.so` loaded in memory. Dropping this unloads the library + _lib: libloading::Library, + pub hooks: PluginHooks, + pub manifest: PluginManifest, + /// Function to free strings allocated by this plugin (defaults to libc::free) + pub free_string: FreeStringFn, +} + +impl PluginInstance { + /// Call a hook on this plugin with safety wrappers (catch_unwind + timeout) + /// + /// Returns `Ok(None)` if plugin returned NULL (no change) + /// Returns `Ok(Some(html))` if plugin returned modified content + /// Returns `Err` on panic, timeout, invalid response, or plugin error + pub fn call_hook(&self, f: PluginFn, input: &str) -> Result> { + let timeout = Duration::from_millis(self.manifest.timeout_ms); + match ffi::call_hook_safe(f, input, timeout) { + Ok(Some(json)) => match ffi::parse_hook_response(&json) { + Ok(Some(html)) => Ok(Some(html)), + Ok(None) => Ok(None), + Err(e) => { + warn!("Plugin '{}' returned invalid response: {}", self.name, e); + Err(e) + } + }, + Ok(None) => Ok(None), + Err(e) => { + warn!("Plugin '{}' hook failed: {}", self.name, e); + Err(e) + } + } + } +} + +/// Manages loaded plugins and dispatches hook calls +pub struct PluginManager { + plugins: Vec, + /// Per-plugin hook call timing: plugin_name -> total Duration + hook_timings: Arc>>, +} + +impl PluginManager { + /// Create an empty plugin manager (no plugins loaded) + pub fn new() -> Self { + Self { + plugins: Vec::new(), + hook_timings: Arc::new(Mutex::new(HashMap::new())), + } + } + + /// Scan `plugins/` under `site_dir` and load all valid plugins + pub fn load(site_dir: &Path) -> Self { + let mut manager = Self::new(); + let plugins_dir = site_dir.join("plugins"); + + if !plugins_dir.is_dir() { + return manager; + } + + let entries = match std::fs::read_dir(&plugins_dir) { + Ok(e) => e, + Err(e) => { + warn!("Failed to read plugins directory: {}", e); + return manager; + } + }; + + for entry in entries.filter_map(|e| e.ok()) { + if !entry.file_type().map(|ft| ft.is_dir()).unwrap_or(false) { + continue; + } + let dir = entry.path(); + match load_plugin(&dir) { + Ok(instance) => { + info!( + "Loaded plugin '{}' v{}", + instance.name, instance.version + ); + manager.plugins.push(instance); + } + Err(e) => { + warn!( + "Plugin '{}' skipped: {}", + dir.file_name() + .and_then(|n| n.to_str()) + .unwrap_or("?"), + e + ); + } + } + } + + manager.plugins.sort_by_key(|p| p.manifest.priority); + + manager + } + + /// Iterate over loaded plugins + pub fn plugins(&self) -> impl Iterator { + self.plugins.iter() + } + + /// Number of loaded plugins + pub fn len(&self) -> usize { + self.plugins.len() + } + + /// Whether no plugins are loaded + pub fn is_empty(&self) -> bool { + self.plugins.is_empty() + } + + /// Check if any plugin declares a given hook bit + pub fn has_hook(&self, hook_bit: u32) -> bool { + self.plugins + .iter() + .any(|p| p.manifest.hooks.to_mask() & hook_bit != 0) + } + + /// Call a hook on a plugin with timing recorded + pub fn call_hook(&self, plugin: &PluginInstance, f: PluginFn, input: &str) -> Result> { + let start = Instant::now(); + let result = plugin.call_hook(f, input); + self.record_hook_time(&plugin.name, start.elapsed()); + result + } + + /// Record hook call duration for a plugin (thread-safe) + pub fn record_hook_time(&self, plugin_name: &str, duration: Duration) { + if let Ok(mut timings) = self.hook_timings.lock() { + *timings.entry(plugin_name.to_string()).or_default() += duration; + } + } + + /// Get per-plugin hook timings (name -> total duration) + pub fn hook_timings(&self) -> HashMap { + self.hook_timings + .lock() + .map(|guard| guard.clone()) + .unwrap_or_default() + } +} + +impl Default for PluginManager { + fn default() -> Self { + Self::new() + } +} + +pub fn library_extension() -> &'static str { + #[cfg(target_os = "linux")] + { "so" } + #[cfg(target_os = "macos")] + { "dylib" } + #[cfg(target_os = "windows")] + { "dll" } + #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))] + { "so" } +} + +pub fn library_filename(name: &str) -> String { + // Linux/macOS convention: lib. + // Windows convention: .dll + #[cfg(target_os = "windows")] + { format!("{}.{}", name, library_extension()) } + #[cfg(not(target_os = "windows"))] + { format!("lib{}.{}", name, library_extension()) } +} + +/// Find the shared library file in a plugin directory +fn find_library(dir: &Path, name: &str) -> Option { + let expected = dir.join(library_filename(name)); + if expected.is_file() { + return Some(expected); + } + // Fallback: scan for any .so/.dylib/.dll in the directory + let ext = library_extension(); + std::fs::read_dir(dir) + .ok()? + .filter_map(|e| e.ok()) + .find(|e| { + e.path() + .extension() + .and_then(|s| s.to_str()) + .map(|s| s == ext) + .unwrap_or(false) + }) + .map(|e| e.path()) +} + +/// Default free function matching libc::free signature +extern "C" fn default_free(ptr: *mut std::os::raw::c_char) { + unsafe { libc::free(ptr as *mut libc::c_void) } +} + +/// Load a single plugin from a directory containing `plugin.toml` + shared library +fn load_plugin(dir: &Path) -> eyre::Result { + let manifest_path = dir.join("plugin.toml"); + if !manifest_path.is_file() { + eyre::bail!("no plugin.toml found"); + } + + let manifest = PluginManifest::load(&manifest_path)?; + manifest.validate_abi()?; + manifest.validate_semver()?; + + let lib_path = find_library(dir, &manifest.plugin.name) + .ok_or_else(|| eyre::eyre!("shared library not found"))?; + + // SAFETY: we validate ABI before loading, and the init function is the only symbol we look up + let lib = unsafe { libloading::Library::new(&lib_path) } + .map_err(|e| eyre::eyre!("failed to load {}: {}", lib_path.display(), e))?; + + type InitFn = unsafe extern "C" fn( + *mut PluginInfo, + *mut u32, + *mut [Option; 4], + ); + + let init: libloading::Symbol = unsafe { lib.get(b"norgolith_plugin_init") } + .map_err(|e| eyre::eyre!("missing symbol norgolith_plugin_init: {}", e))?; + + let mut info = PluginInfo { + abi_version: 0, + name: std::ptr::null(), + version: std::ptr::null(), + }; + let mut hook_mask = 0u32; + let mut hooks: [Option; 4] = [None, None, None, None]; + + unsafe { init(&mut info, &mut hook_mask, &mut hooks) }; + + // Validate that the returned ABI matches what the manifest claims + if info.abi_version != manifest.plugin.abi { + warn!( + "Plugin '{}' returned abi={} but manifest declares abi={}", + manifest.plugin.name, info.abi_version, manifest.plugin.abi + ); + } + + // Validate hook mask matches manifest declarations + let declared_mask = manifest.hooks.to_mask(); + if hook_mask != declared_mask { + warn!( + "Plugin '{}' hook mask mismatch: manifest declares {:#x}, plugin returned {:#x}", + manifest.plugin.name, declared_mask, hook_mask + ); + } + + let plugin_hooks = PluginHooks { + pre_build: if hook_mask & HOOK_PRE_BUILD != 0 { + hooks[0] + } else { + None + }, + post_convert: if hook_mask & HOOK_POST_CONVERT != 0 { + hooks[1] + } else { + None + }, + post_render: if hook_mask & HOOK_POST_RENDER != 0 { + hooks[2] + } else { + None + }, + post_build: if hook_mask & HOOK_POST_BUILD != 0 { + hooks[3] + } else { + None + }, + }; + + Ok(PluginInstance { + name: manifest.plugin.name.clone(), + version: manifest.plugin.version.clone(), + _lib: lib, + hooks: plugin_hooks, + manifest, + free_string: default_free, + }) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn target_debug() -> PathBuf { + let out_dir = PathBuf::from(env!("OUT_DIR")); + out_dir.parent().unwrap().parent().unwrap().to_path_buf() + } + + fn write_test_manifest(dir: &Path, name: &str) { + let manifest = format!( + r#"[plugin] +name = "{name}" +version = "0.1.0" +norgolith = ">=0.4.0" +abi = 1 + +[hooks] +pre_build = false +post_convert = false +post_render = true +post_build = false + +[capabilities] +filesystem = "none" +network = false + +timeout_ms = 5000 +"# + ); + std::fs::write(dir.join("plugin.toml"), manifest).unwrap(); + } + + #[test] + fn test_empty_plugins_dir() { + let tmp = tempfile::tempdir().unwrap(); + let mgr = PluginManager::load(tmp.path()); + assert!(mgr.is_empty()); + } + + #[test] + fn test_missing_plugins_dir() { + let tmp = tempfile::tempdir().unwrap(); + let mgr = PluginManager::load(&tmp.path().join("nonexistent")); + assert!(mgr.is_empty()); + } + + #[test] + fn test_plugin_dir_without_manifest() { + let tmp = tempfile::tempdir().unwrap(); + let plugin_dir = tmp.path().join("plugins").join("broken"); + std::fs::create_dir_all(&plugin_dir).unwrap(); + let mgr = PluginManager::load(tmp.path()); + assert!(mgr.is_empty()); + } + + #[test] + fn test_load_ok_plugin() { + let tmp = tempfile::tempdir().unwrap(); + let plugin_dir = tmp.path().join("plugins").join("test-ok"); + std::fs::create_dir_all(&plugin_dir).unwrap(); + write_test_manifest(&plugin_dir, "test-ok"); + + let lib_name = library_filename("test-ok"); + let src = target_debug().join(&lib_name); + if !src.is_file() { + eprintln!("test plugin not compiled, skipping"); + return; + } + std::fs::copy(&src, plugin_dir.join(&lib_name)).unwrap(); + + let mgr = PluginManager::load(tmp.path()); + assert_eq!(mgr.len(), 1); + let p = mgr.plugins().next().unwrap(); + assert_eq!(p.name, "test-ok"); + assert!(p.hooks.post_render.is_some()); + } + + #[test] + fn test_hook_ok_transform() { + let tmp = tempfile::tempdir().unwrap(); + let plugin_dir = tmp.path().join("plugins").join("test-ok"); + std::fs::create_dir_all(&plugin_dir).unwrap(); + write_test_manifest(&plugin_dir, "test-ok"); + + let lib_name = library_filename("test-ok"); + let src = target_debug().join(&lib_name); + if !src.is_file() { + eprintln!("test plugin not compiled, skipping"); + return; + } + std::fs::copy(&src, plugin_dir.join(&lib_name)).unwrap(); + + let mgr = PluginManager::load(tmp.path()); + let p = mgr.plugins().next().unwrap(); + let input = r#"{"html":"

hello

","metadata":{},"rel_path":"test.norg"}"#; + let result = p.call_hook(p.hooks.post_render.unwrap(), input).unwrap(); + assert!(result.is_some()); + assert!(result.unwrap().contains("[transformed]")); + } + + #[test] + fn test_hook_null_returns_none() { + let tmp = tempfile::tempdir().unwrap(); + let plugin_dir = tmp.path().join("plugins").join("test-null"); + std::fs::create_dir_all(&plugin_dir).unwrap(); + write_test_manifest(&plugin_dir, "test-null"); + + let lib_name = library_filename("test-null"); + let src = target_debug().join(&lib_name); + if !src.is_file() { + eprintln!("test plugin not compiled, skipping"); + return; + } + std::fs::copy(&src, plugin_dir.join(&lib_name)).unwrap(); + + let mgr = PluginManager::load(tmp.path()); + let p = mgr.plugins().next().unwrap(); + let input = r#"{"html":"

hello

","metadata":{},"rel_path":"test.norg"}"#; + let result = p.call_hook(p.hooks.post_render.unwrap(), input).unwrap(); + assert_eq!(result, None); + } + + #[test] + fn test_hook_timeout() { + let tmp = tempfile::tempdir().unwrap(); + let plugin_dir = tmp.path().join("plugins").join("test-timeout"); + std::fs::create_dir_all(&plugin_dir).unwrap(); + write_test_manifest(&plugin_dir, "test-timeout"); + + let lib_name = library_filename("test-timeout"); + let src = target_debug().join(&lib_name); + if !src.is_file() { + eprintln!("test plugin not compiled, skipping"); + return; + } + std::fs::copy(&src, plugin_dir.join(&lib_name)).unwrap(); + + let mgr = PluginManager::load(tmp.path()); + let p = mgr.plugins().next().unwrap(); + let input = r#"{"html":"

hello

","metadata":{},"rel_path":"test.norg"}"#; + // Should return Err (timeout) + let result = p.call_hook(p.hooks.post_render.unwrap(), input); + assert!(result.is_err()); + } + + #[test] + fn test_hook_error_response() { + let tmp = tempfile::tempdir().unwrap(); + let plugin_dir = tmp.path().join("plugins").join("test-error"); + std::fs::create_dir_all(&plugin_dir).unwrap(); + write_test_manifest(&plugin_dir, "test-error"); + + let lib_name = library_filename("test-error"); + let src = target_debug().join(&lib_name); + if !src.is_file() { + eprintln!("test plugin not compiled, skipping"); + return; + } + std::fs::copy(&src, plugin_dir.join(&lib_name)).unwrap(); + + let mgr = PluginManager::load(tmp.path()); + let p = mgr.plugins().next().unwrap(); + let input = r#"{"html":"

hello

","metadata":{},"rel_path":"test.norg"}"#; + // Error response -> call_hook returns Err + let result = p.call_hook(p.hooks.post_render.unwrap(), input); + assert!(result.is_err()); + } + + #[test] + fn test_sdk_plugin_load() { + let tmp = tempfile::tempdir().unwrap(); + let plugin_dir = tmp.path().join("plugins").join("test-sdk-plugin"); + std::fs::create_dir_all(&plugin_dir).unwrap(); + + // Write manifest for the SDK plugin + let manifest = r#"[plugin] +name = "test-sdk-plugin" +version = "0.1.0" +norgolith = ">=0.4.0" +abi = 1 + +[hooks] +pre_build = false +post_convert = false +post_render = true +post_build = false + +[capabilities] +filesystem = "none" +network = false + +timeout_ms = 5000 +"#; + std::fs::write(plugin_dir.join("plugin.toml"), manifest).unwrap(); + + // Find the compiled SDK plugin library + let out_dir = PathBuf::from(env!("OUT_DIR")); + let target_dir = out_dir.parent().unwrap().parent().unwrap(); + let release_dir = target_dir.parent().unwrap().join("release"); + + let lib_name = library_filename("test-sdk-plugin"); + let src = release_dir.join(&lib_name); + if !src.is_file() { + eprintln!("SDK test plugin not compiled, skipping"); + return; + } + std::fs::copy(&src, plugin_dir.join(&lib_name)).unwrap(); + + let mgr = PluginManager::load(tmp.path()); + assert_eq!(mgr.len(), 1, "should load exactly one plugin"); + + let p = mgr.plugins().next().unwrap(); + assert_eq!(p.name, "test-sdk-plugin"); + assert_eq!(p.version, "0.1.0"); + assert!(p.hooks.post_render.is_some(), "post_render hook should be set"); + + let input = r#"{"html":"

hello

","metadata":{},"rel_path":"test.norg"}"#; + let result = p.call_hook(p.hooks.post_render.unwrap(), input).unwrap(); + assert!(result.is_some(), "plugin should return modified HTML"); + assert!(result.unwrap().contains(""), "should contain plugin marker"); + } +} diff --git a/core/src/plugin/sandbox.rs b/core/src/plugin/sandbox.rs new file mode 100644 index 0000000..a3b5e8e --- /dev/null +++ b/core/src/plugin/sandbox.rs @@ -0,0 +1,126 @@ +use std::path::Path; + +use eyre::Result; +use tracing::warn; + +/// Apply Landlock filesystem restrictions to the current process +/// +/// Restricts access to: +/// - `site_dir` (read/write) +/// - `site_dir/public` (write output) +/// - `site_dir/plugins` (read .so files) +/// - Cache directory (read/write) +/// +/// Once applied, restrictions are irreversible for the process lifetime. +/// On non-Linux platforms or without `sandbox-linux` feature, this is a no-op. +/// +/// # Known Limitation +/// Landlock is applied AFTER plugin loading (`dlopen`). Plugin `.so` constructors +/// (`init_array`) execute during loading, before restrictions are in place. +/// This means plugin init code runs with full filesystem access. +/// Mitigation: plugin init functions should be minimal (set hook pointers only). +/// A future version can fork before loading to isolate constructors. +pub fn apply_landlock(site_dir: &Path) -> Result<()> { + #[cfg(not(all(target_os = "linux", feature = "sandbox-linux")))] + { + let _ = site_dir; + Ok(()) + } + + #[cfg(all(target_os = "linux", feature = "sandbox-linux"))] + { + if !landlock_available() { + warn!( + "Landlock unavailable (kernel too old?). \ + Plugins running without filesystem confinement." + ); + return Ok(()); + } + + use landlock::*; + + let public_dir = site_dir.join("public"); + let plugins_dir = site_dir.join("plugins"); + let cache_dir = dirs::cache_dir() + .unwrap_or_else(std::env::temp_dir) + .join("norgolith"); + + // Ensure dirs exist for path_beneath_rules (PathFd::new requires the path) + let _ = std::fs::create_dir_all(&public_dir); + let _ = std::fs::create_dir_all(&plugins_dir); + let _ = std::fs::create_dir_all(&cache_dir); + + let allowed_paths: Vec<&Path> = vec![ + site_dir, + &public_dir, + &plugins_dir, + &cache_dir, + ]; + + let access = AccessFs::from_all(ABI::V1); + + let ruleset = Ruleset::default() + .handle_access(access) + .map_err(|e| eyre::eyre!("failed to create landlock ruleset: {}", e))? + .create() + .map_err(|e| eyre::eyre!("failed to create landlock ruleset: {}", e))?; + + let rules = path_beneath_rules(&allowed_paths, access); + + let ruleset = ruleset + .add_rules(rules) + .map_err(|e| eyre::eyre!("failed to add landlock rules: {}", e))?; + + match ruleset.restrict_self() { + Ok(status) => { + if status.ruleset != RulesetStatus::FullyEnforced { + warn!( + "Landlock partially enforced (kernel feature gap). \ + Some restrictions may not apply." + ); + } + } + Err(e) => { + warn!( + "Failed to apply Landlock restrictions: {}. \ + Plugins running without filesystem confinement.", + e + ); + } + } + + Ok(()) + } +} + +/// Check if Landlock is available on this system +#[cfg(all(target_os = "linux", feature = "sandbox-linux"))] +fn landlock_available() -> bool { + // Try the landlock_create_ruleset syscall with version query + let ret = unsafe { + libc::syscall( + libc::SYS_landlock_create_ruleset, + std::ptr::null::(), + 0usize, + 1u32, // LANDLOCK_CREATE_RULESET_VERSION + ) + }; + // ret > 0 means supported, ENOSYS means not supported + if ret > 0 { + return true; + } + let errno = unsafe { *libc::__errno_location() }; + errno != libc::ENOSYS +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_apply_landlock_no_panic() { + let tmp = tempfile::tempdir().unwrap(); + // Should not panic, even if Landlock is unavailable + let _ = apply_landlock(tmp.path()); + } +} diff --git a/src/resources/assets/livereload.js b/core/src/resources/assets/livereload.js similarity index 100% rename from src/resources/assets/livereload.js rename to core/src/resources/assets/livereload.js diff --git a/src/resources/assets/style.css b/core/src/resources/assets/style.css similarity index 100% rename from src/resources/assets/style.css rename to core/src/resources/assets/style.css diff --git a/src/resources/content/index.norg b/core/src/resources/content/index.norg similarity index 100% rename from src/resources/content/index.norg rename to core/src/resources/content/index.norg diff --git a/src/resources/templates/base.html b/core/src/resources/templates/base.html similarity index 100% rename from src/resources/templates/base.html rename to core/src/resources/templates/base.html diff --git a/src/resources/templates/categories.html b/core/src/resources/templates/categories.html similarity index 100% rename from src/resources/templates/categories.html rename to core/src/resources/templates/categories.html diff --git a/src/resources/templates/category.html b/core/src/resources/templates/category.html similarity index 100% rename from src/resources/templates/category.html rename to core/src/resources/templates/category.html diff --git a/src/resources/templates/default.html b/core/src/resources/templates/default.html similarity index 100% rename from src/resources/templates/default.html rename to core/src/resources/templates/default.html diff --git a/src/resources/templates/rss.xml b/core/src/resources/templates/rss.xml similarity index 100% rename from src/resources/templates/rss.xml rename to core/src/resources/templates/rss.xml diff --git a/src/schema/mod.rs b/core/src/schema/mod.rs similarity index 100% rename from src/schema/mod.rs rename to core/src/schema/mod.rs diff --git a/src/schema/validator.rs b/core/src/schema/validator.rs similarity index 100% rename from src/schema/validator.rs rename to core/src/schema/validator.rs diff --git a/src/shared/mod.rs b/core/src/shared/mod.rs similarity index 100% rename from src/shared/mod.rs rename to core/src/shared/mod.rs diff --git a/src/tera_functions.rs b/core/src/tera_functions.rs similarity index 100% rename from src/tera_functions.rs rename to core/src/tera_functions.rs diff --git a/src/theme.rs b/core/src/theme.rs similarity index 100% rename from src/theme.rs rename to core/src/theme.rs diff --git a/core/tests/plugins/test_abort.c b/core/tests/plugins/test_abort.c new file mode 100644 index 0000000..3cc8ec5 --- /dev/null +++ b/core/tests/plugins/test_abort.c @@ -0,0 +1,23 @@ +#include + +typedef struct { + unsigned int abi_version; + const char* name; + const char* version; +} PluginInfo; + +typedef char* (*PluginFn)(const char*); + +// Simulates a crash (abort is not catchable by catch_unwind, but tests the path) +char* hook_post_render(const char* input_json) { + abort(); + return NULL; +} + +void norgolith_plugin_init(PluginInfo* info, unsigned int* hook_mask, PluginFn hooks[4]) { + info->abi_version = 1; + info->name = "test-abort"; + info->version = "0.1.0"; + *hook_mask = 4; // POST_RENDER + hooks[2] = hook_post_render; +} diff --git a/core/tests/plugins/test_error.c b/core/tests/plugins/test_error.c new file mode 100644 index 0000000..057730e --- /dev/null +++ b/core/tests/plugins/test_error.c @@ -0,0 +1,26 @@ +#include +#include + +typedef struct { + unsigned int abi_version; + const char* name; + const char* version; +} PluginInfo; + +typedef char* (*PluginFn)(const char*); + +// Returns an error JSON +char* hook_post_render(const char* input_json) { + const char* err = "{\"status\":\"error\",\"message\":\"intentional test error\"}"; + char* result = malloc(strlen(err) + 1); + strcpy(result, err); + return result; +} + +void norgolith_plugin_init(PluginInfo* info, unsigned int* hook_mask, PluginFn hooks[4]) { + info->abi_version = 1; + info->name = "test-error"; + info->version = "0.1.0"; + *hook_mask = 4; // POST_RENDER + hooks[2] = hook_post_render; +} diff --git a/core/tests/plugins/test_null.c b/core/tests/plugins/test_null.c new file mode 100644 index 0000000..6e92f19 --- /dev/null +++ b/core/tests/plugins/test_null.c @@ -0,0 +1,22 @@ +#include + +typedef struct { + unsigned int abi_version; + const char* name; + const char* version; +} PluginInfo; + +typedef char* (*PluginFn)(const char*); + +// Returns NULL (no change) +char* hook_post_render(const char* input_json) { + return NULL; +} + +void norgolith_plugin_init(PluginInfo* info, unsigned int* hook_mask, PluginFn hooks[4]) { + info->abi_version = 1; + info->name = "test-null"; + info->version = "0.1.0"; + *hook_mask = 4; // POST_RENDER + hooks[2] = hook_post_render; +} diff --git a/core/tests/plugins/test_ok.c b/core/tests/plugins/test_ok.c new file mode 100644 index 0000000..93e890f --- /dev/null +++ b/core/tests/plugins/test_ok.c @@ -0,0 +1,40 @@ +#include +#include + +typedef struct { + unsigned int abi_version; + const char* name; + const char* version; +} PluginInfo; + +typedef char* (*PluginFn)(const char*); + +// post_render hook: echoes input back with " [transformed]" appended to html +char* hook_post_render(const char* input_json) { + // Find "html" field value (simple parse for testing) + const char* html_start = strstr(input_json, "\"html\":\""); + if (!html_start) return NULL; + html_start += 8; + + const char* html_end = strchr(html_start, '"'); + if (!html_end) return NULL; + + size_t html_len = html_end - html_start; + size_t suffix_len = strlen(" [transformed]"); + + char* result = malloc(html_len + suffix_len + 34); // room for JSON wrapper + memcpy(result, "{\"html\":\"", 9); + memcpy(result + 9, html_start, html_len); + memcpy(result + 9 + html_len, " [transformed]\"", 16); + result[9 + html_len + 15] = '}'; + result[9 + html_len + 16] = '\0'; + return result; +} + +void norgolith_plugin_init(PluginInfo* info, unsigned int* hook_mask, PluginFn hooks[4]) { + info->abi_version = 1; + info->name = "test-ok"; + info->version = "0.1.0"; + *hook_mask = 4; // POST_RENDER = bit 2 + hooks[2] = hook_post_render; +} diff --git a/core/tests/plugins/test_sdk_plugin/Cargo.lock b/core/tests/plugins/test_sdk_plugin/Cargo.lock new file mode 100644 index 0000000..f836c0d --- /dev/null +++ b/core/tests/plugins/test_sdk_plugin/Cargo.lock @@ -0,0 +1,122 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "memchr" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" + +[[package]] +name = "norgolith-plugin-sdk" +version = "0.1.0" +dependencies = [ + "libc", + "serde", + "serde_json", +] + +[[package]] +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.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "serde" +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 = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "syn" +version = "2.0.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "test_sdk_plugin" +version = "0.1.0" +dependencies = [ + "norgolith-plugin-sdk", + "serde_json", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/core/tests/plugins/test_sdk_plugin/Cargo.toml b/core/tests/plugins/test_sdk_plugin/Cargo.toml new file mode 100644 index 0000000..edea83c --- /dev/null +++ b/core/tests/plugins/test_sdk_plugin/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "test_sdk_plugin" +version = "0.1.0" +edition = "2021" + +[workspace] + +[lib] +crate-type = ["cdylib"] + +[dependencies] +norgolith-plugin-sdk = { path = "../../../../sdk" } +serde_json = "1.0" diff --git a/core/tests/plugins/test_sdk_plugin/src/lib.rs b/core/tests/plugins/test_sdk_plugin/src/lib.rs new file mode 100644 index 0000000..60b6541 --- /dev/null +++ b/core/tests/plugins/test_sdk_plugin/src/lib.rs @@ -0,0 +1,11 @@ +use norgolith_plugin_sdk::*; + +fn post_render_handler(json: serde_json::Value) -> Result, String> { + let ctx: TransformContext = serde_json::from_value(json).map_err(|e| e.to_string())?; + // Echo back the HTML with a marker + Ok(Some(format!("{}", ctx.html))) +} + +register_plugin!("test-sdk-plugin", "0.1.0", + hooks: [post_render: post_render_handler] +); diff --git a/core/tests/plugins/test_timeout.c b/core/tests/plugins/test_timeout.c new file mode 100644 index 0000000..baa707e --- /dev/null +++ b/core/tests/plugins/test_timeout.c @@ -0,0 +1,23 @@ +#include + +typedef struct { + unsigned int abi_version; + const char* name; + const char* version; +} PluginInfo; + +typedef char* (*PluginFn)(const char*); + +// Infinite loop to trigger timeout +char* hook_post_render(const char* input_json) { + while (1) {} + return NULL; +} + +void norgolith_plugin_init(PluginInfo* info, unsigned int* hook_mask, PluginFn hooks[4]) { + info->abi_version = 1; + info->name = "test-timeout"; + info->version = "0.1.0"; + *hook_mask = 4; // POST_RENDER + hooks[2] = hook_post_render; +} diff --git a/docs/content/docs/plugins.norg b/docs/content/docs/plugins.norg new file mode 100644 index 0000000..5f4f408 --- /dev/null +++ b/docs/content/docs/plugins.norg @@ -0,0 +1,345 @@ +@document.meta +title: Plugins +description: Extend Norgolith with plugins +authors: [ + NTBBloodbath +] +categories: [ + docs +] +created: 2026-06-28T00:00:00-04:00 +updated: 2026-06-28T00:00:00-04:00 +draft: false +layout: docs +version: 1.0.0 +@end + +Norgolith has a plugin system that lets you extend the build process. Plugins are shared libraries +written in Rust that hook into different stages of the build pipeline. They can transform content, +inject HTML, generate files, and more. + +This guide explains how plugins work, how to install them, and how to write your own. + +** How Plugins Work + +A plugin is a shared library (`.so`, `.dylib`, or `.dll`) with a `plugin.toml` manifest. When you +run `lith build`, Norgolith loads all plugins from the `plugins/` directory, checks their manifests, +and calls their hook functions at the right time in the build process. + +The build pipeline runs in this order: + +@code bash +Norg files -> [pre_build] -> parse + convert to HTML -> [post_convert] + -> Tera template -> [post_render] -> write to public/ -> [post_build] +@end + +Each plugin can implement one or more of these four hooks. The hooks receive JSON input with the +current page data, and return modified HTML or an error. + +** Quick Start + +Let's walk through creating a simple plugin that adds a watermark to every page. + +*** Scaffold the plugin + +Run the `lith plugin new` command to create the project structure: + +@code bash +lith plugin new watermark +@end + +This creates a `plugins/watermark/` directory with three files: + +@code bash +plugins/watermark/ ++-- plugin.toml # Plugin manifest ++-- Cargo.toml # Rust package config ++-- src/lib.rs # Your code goes here +@end + +*** Edit the hook + +Open `plugins/watermark/src/lib.rs` and replace its contents with: + +@code rust +use norgolith_plugin_sdk::*; + +register_plugin!("watermark", "0.1.0", + hooks: [post_render: add_watermark] +); + +fn add_watermark(json: serde_json::Value) -> Result, String> { + let ctx: TransformContext = serde_json::from_value(json) + .map_err(|e| e.to_string())?; + + let watermark = ""; + Ok(Some(format!("{}\n{}", ctx.html, watermark))) +} +@end + +*** Build and install + +Build the plugin in release mode, then install it: + +@code bash +cd plugins/watermark +cargo build --release +cd ../.. +lith plugin install plugins/watermark +@end + +*** Test it + +Run `lith build` and check the output. Every page will now have the comment at the end. + +** Plugin Manifest + +Every plugin needs a `plugin.toml` file next to its shared library. Here is the full format: + +@code toml +[plugin] +name = "my-plugin" +version = "0.1.0" +norgolith = ">=0.4.0" +abi = 1 + +[hooks] +pre_build = false +post_convert = false +post_render = true +post_build = false + +[capabilities] +filesystem = "none" +network = false + +timeout_ms = 10000 +priority = 100 +@end + +*** \[plugin\] section + +- `name`: must match the name you pass to `register_plugin!` in your Rust code +- `version`: semantic version for your plugin +- `norgolith`: semver requirement for the Norgolith version this plugin supports (e.g. `>=0.4.0`) +- `abi`: must be `1`. This is the plugin ABI version and must match the core + +*** \[hooks\] section + +Set each hook to `true` if your plugin implements it: + +- `pre_build`: runs before any content is processed +- `post_convert`: runs after Norg-to-HTML conversion, before Tera templating +- `post_render`: runs after Tera layout is applied, before writing to disk +- `post_build`: runs after all pages are written + +*** \[capabilities\] section + +Declare what your plugin needs: + +- `filesystem`: `"none"`, `"read"`, `"write"`, or `"read-write"` +- `network`: `true` or `false` + +@embed html +
+ + + Note + +@end + +These are declarations only. Norgolith currently does not enforce filesystem or network restrictions +at runtime. Landlock sandboxing on Linux restricts filesystem access to the site directory. +@embed html +
+@end + +*** Other fields + +- `timeout_ms`: maximum time in milliseconds for a single hook call (default: 10000) +- `priority`: execution order when multiple plugins are installed. Lower numbers run first (default: + 100) + +** Hook Reference + +*** pre_build + +Runs once before any content is processed. Receives the full site configuration as JSON. Use this to +initialize state, load external data, or validate the environment. + +@code rust +fn init(json: serde_json::Value) -> Result, String> { + let ctx: PreBuildContext = serde_json::from_value(json) + .map_err(|e| e.to_string())?; + + // ctx.site_config -> full site config as JSON + // ctx.pages_dir -> path to content/ directory + // ctx.output_dir -> path to public/ directory + + Ok(None) // Return Ok(None) if you have nothing to report +} +@end + +*** post_convert + +Runs after each page is converted from Norg to HTML, but before Tera templating. This is where you +transform the page content. The input is an HTML fragment (just the body content, no layout). + +@code rust +fn transform(json: serde_json::Value) -> Result, String> { + let ctx: TransformContext = serde_json::from_value(json) + .map_err(|e| e.to_string())?; + + // ctx.html -> the HTML content + // ctx.metadata -> page metadata as JSON (title, date, tags, etc.) + // ctx.rel_path -> relative path (e.g. "posts/hello.norg") + + // Return the modified HTML + Ok(Some(ctx.html.replace("

", "

"))) +} +@end + +*** post_render + +Runs after Tera layout is applied. The input is the complete HTML page (including ``, +``, etc.). Use this to inject elements into `` or modify the final page. + +@code rust +fn inject(json: serde_json::Value) -> Result, String> { + let ctx: TransformContext = serde_json::from_value(json) + .map_err(|e| e.to_string())?; + + // Inject before + if let Some(pos) = ctx.html.find("") { + let tag = r#""#; + let result = format!("{}{}\n{}", &ctx.html[..pos], tag, &ctx.html[pos..]); + return Ok(Some(result)); + } + + Ok(None) +} +@end + +*** post_build + +Runs once after all pages are written to disk. Use this to generate extra files or clean up. + +@code rust +fn finalize(json: serde_json::Value) -> Result, String> { + let ctx: PostBuildContext = serde_json::from_value(json) + .map_err(|e| e.to_string())?; + + // Generate a file in the output directory + let path = std::path::Path::new(&ctx.output_dir).join("robots.txt"); + std::fs::write(&path, "User-agent: *\nAllow: /").ok(); + + Ok(None) +} +@end + +** CLI Commands + +*** lith plugin new \ + +Create a new plugin project with the basic structure. + +@code bash +lith plugin new my-plugin +@end + +*** lith plugin install \ + +Build a plugin from source and install it into your site's `plugins/` directory. + +@code bash +lith plugin install plugins/my-plugin +@end + +*** lith plugin list + +Show all installed plugins with their name, version, hooks, priority, and status. + +@code bash +lith plugin list +@end + +*** lith plugin uninstall \ + +Remove an installed plugin. + +@code bash +lith plugin uninstall my-plugin +@end + +** Plugin Ordering + +When you have multiple plugins installed, they run in the order specified by the `priority` field in +`plugin.toml`. Lower numbers run first. + +For example, if you have two plugins: + +@code toml +# Plugin A: priority 50 +priority = 50 + +# Plugin B: priority 100 +priority = 100 +@end + +Plugin A's hooks run before Plugin B's hooks. The default priority is 100 if you don't specify one. + +** Security + +Plugins run inside the Norgolith process. On Linux, Landlock restricts filesystem access to the site +directory and cache directory. Plugins cannot read or write files outside these paths. + +@embed html +

+ + + Known Limitation + +@end + +Network restrictions are not enforced right now. The `network` field in `plugin.toml` is a +declaration only. Plugins can open network connections regardless of this setting. + +@embed html +
+@end + +** Troubleshooting + +*** Plugin not loading + +- Check that `plugin.toml` exists next to the `.so` file +- Make sure the `abi` field in `plugin.toml` matches the current Norgolith version +- Check the build output for warnings about skipped plugins + +*** Plugin hook not running + +- Verify that the hook is set to `true` in `plugin.toml` +- Check that your `register_plugin!` macro includes the hook name + +*** Build is slow with plugins + +- Use the `priority` field to order plugins efficiently +- Check the build output for per-plugin timing information +- Consider whether your plugin needs to run on every page + +*** Error: "ABI mismatch" + +The plugin was compiled against a different ABI version. Rebuild the plugin from source with the +current Norgolith SDK. + +*** Error: "Version mismatch" + +The plugin requires a different Norgolith version. Check the `norgolith` field in `plugin.toml` and +update it if needed, or install a compatible version of the plugin. + +** Learn More + +- {/sdk/}[Plugin SDK on crates.io] - API reference for plugin developers +- {https://github.com/NTBBloodbath/norgolith}[Norgolith repository] - source code and issue tracker + +{:/docs/contributing:}[Next: Contributing ->] diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml new file mode 100644 index 0000000..e427739 --- /dev/null +++ b/sdk/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "norgolith-plugin-sdk" +version = "0.1.0" +edition = "2021" +authors = ["NTBBloodbath Result, String> { + let ctx: TransformContext = serde_json::from_value(json) + .map_err(|e| e.to_string())?; + Ok(Some(ctx.html)) +} +``` + +Build and install it: + +```bash +cd plugins/my-plugin +cargo build --release +cd ../.. +lith plugin install plugins/my-plugin +``` + +Now `lith build` runs your plugin on every page. + +## The `register_plugin!` Macro + +The macro generates two things: the shared library entry point and bridge functions for each hook. + +```rust +register_plugin!("plugin-name", "0.1.0", + hooks: [hook_name: handler_function, ...] +); +``` + +- First argument: plugin name (must match `plugin.toml`) +- Second argument: version string +- `hooks:` list: maps hook names to your handler functions + +Valid hook names are `pre_build`, `post_convert`, `post_render`, and `post_build`. + +## Hook Types + +Each hook runs at a different point in the build process: + +| Hook | When it runs | Input | Use case | +| -------------- | -------------------------------------------- | ------------------------ | ----------------------------------------------- | +| `pre_build` | Before any content is processed | Site config | Initialize plugin state, load external data | +| `post_convert` | After Norg-to-HTML, before Tera templating | HTML fragment + metadata | Modify page content (e.g., syntax highlighting) | +| `post_render` | After Tera layout is applied, before writing | Final HTML page | Inject into `` or `` | +| `post_build` | After all pages are written to disk | Site config | Generate extra files (e.g., sitemaps) | + +## Context Types + +Each hook receives a JSON string. Deserialize it into the matching context type: + +### `PreBuildContext` + +Available in `pre_build`: + +```rust +#[derive(serde::Deserialize)] +pub struct PreBuildContext { + pub site_config: serde_json::Value, // Full site config as JSON + pub pages_dir: String, // Path to content/ directory + pub output_dir: String, // Path to public/ directory +} +``` + +### `TransformContext` + +Available in `post_convert` and `post_render`: + +```rust +#[derive(serde::Deserialize)] +pub struct TransformContext { + pub html: String, // The HTML content + pub metadata: serde_json::Value, // Page metadata (title, date, etc.) + pub rel_path: String, // Relative path (e.g., "posts/hello.norg") +} +``` + +### `PostBuildContext` + +Available in `post_build`: + +```rust +#[derive(serde::Deserialize)] +pub struct PostBuildContext { + pub site_config: serde_json::Value, + pub pages_dir: String, + pub output_dir: String, +} +``` + +## Return Values + +Your handler function returns `Result, String>`: + +- `Ok(Some(html))`: return modified content. The new HTML replaces the original. +- `Ok(None)`: no change. The page passes through unmodified. +- `Err(message)`: an error occurred. The error is logged and the page passes through unchanged. + +```rust +fn my_hook(json: serde_json::Value) -> Result, String> { + let ctx: TransformContext = serde_json::from_value(json) + .map_err(|e| e.to_string())?; + + // Only modify pages with a specific tag + if ctx.metadata.get("tags") + .and_then(|v| v.as_array()) + .map(|tags| tags.iter().any(|t| t.as_str() == Some("special"))) + .unwrap_or(false) + { + Ok(Some(format!("\n{}", ctx.html))) + } else { + Ok(None) // Leave other pages alone + } +} +``` + +## Building + +Build your plugin in release mode: + +```bash +cargo build --release +``` + +The output shared library is in `target/release/`: + +- Linux: `libmy-plugin.so` +- macOS: `libmy-plugin.dylib` +- Windows: `my-plugin.dll` + +Install it: + +```bash +lith plugin install plugins/my-plugin +``` + +This copies the `.so` and `plugin.toml` into your site's `plugins/` directory. + +## Plugin Manifest (`plugin.toml`) + +Every plugin needs a `plugin.toml` alongside the shared library: + +```toml +[plugin] +name = "my-plugin" +version = "0.1.0" +norgolith = ">=0.4.0" # Semver requirement for norgolith version +abi = 1 # Must match CORE_ABI_VERSION + +[hooks] +pre_build = false +post_convert = false +post_render = true # Set to true for hooks you implement +post_build = false + +[capabilities] +filesystem = "none" # "none", "read", "write", "read-write" +network = false # Whether plugin needs network access + +timeout_ms = 10000 # Max time per hook call (default: 10000ms) +priority = 100 # Lower numbers run first (default: 100) +``` + +## Priority + +Use the `priority` field to control execution order when you have multiple plugins. Lower numbers run first: + +```toml +priority = 50 # Runs early +priority = 100 # Default, runs after priority 50 +priority = 200 # Runs last +``` + +## Full Example + +Here is a complete plugin that wraps every `

` heading in a styled div: + +```rust +use norgolith_plugin_sdk::*; + +register_plugin!("heading-wrapper", "0.1.0", + hooks: [post_render: wrap_headings] +); + +fn wrap_headings(json: serde_json::Value) -> Result, String> { + let ctx: TransformContext = serde_json::from_value(json) + .map_err(|e| e.to_string())?; + + let html = ctx.html.replace( + "

", + r#"

"#, + ); + + Ok(Some(html)) +} +``` + +## See Also + +- [Plugin system guide](https://norgolith.amartin.beer/docs/plugins): full documentation for users and plugin authors +- [Norgolith repository](https://github.com/NTBBloodbath/norgolith): source code and issue tracker diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs new file mode 100644 index 0000000..3db857a --- /dev/null +++ b/sdk/src/lib.rs @@ -0,0 +1,251 @@ +use std::ffi::{CStr, CString}; +use std::os::raw::c_char; + +/// Current ABI version that plugins must target +pub const CORE_ABI_VERSION: u32 = 1; + +/// Hook bit flags for the plugin hook mask +pub const HOOK_PRE_BUILD: u32 = 1; +pub const HOOK_POST_CONVERT: u32 = 2; +pub const HOOK_POST_RENDER: u32 = 4; +pub const HOOK_POST_BUILD: u32 = 8; + +/// C ABI PluginInfo struct returned by `norgolith_plugin_init` +#[repr(C)] +pub struct PluginInfo { + /// Which version of plugin.h this .so was compiled against + pub abi_version: u32, + /// Human-readable plugin name for error messages + pub name: *const c_char, + /// Semantic version for debugging (not validation) + pub version: *const c_char, +} + +/// Function pointer type for plugin hooks +pub type PluginFn = extern "C" fn(*const c_char) -> *mut c_char; + +/// Context for the pre_build hook +#[derive(serde::Deserialize)] +pub struct PreBuildContext { + pub site_config: serde_json::Value, + pub pages_dir: String, + pub output_dir: String, +} + +/// Context for post_convert and post_render hooks +#[derive(serde::Deserialize)] +pub struct TransformContext { + pub html: String, + pub metadata: serde_json::Value, + pub rel_path: String, +} + +/// Context for the post_build hook +#[derive(serde::Deserialize)] +pub struct PostBuildContext { + pub site_config: serde_json::Value, + pub pages_dir: String, + pub output_dir: String, +} + +/// Universal bridge function: reads C input → calls handler → returns JSON/NULL/error +/// +/// The handler receives a `serde_json::Value` and must return: +/// - `Ok(Some(html))` — modified content (returned as `{"html":"..."}`) +/// - `Ok(None)` — no change (returned as NULL) +/// - `Err(msg)` — error (returned as `{"error":"..."}`) +#[allow(clippy::not_unsafe_ptr_arg_deref)] +pub fn __bridge_json(input: *const c_char, handler: F) -> *mut c_char +where + F: FnOnce(serde_json::Value) -> Result, String>, +{ + let input_str = unsafe { CStr::from_ptr(input) } + .to_str() + .unwrap_or("{}"); + + let value: serde_json::Value = + serde_json::from_str(input_str).unwrap_or(serde_json::Value::Null); + + match handler(value) { + Ok(Some(html)) => { + let output = serde_json::json!({"html": html}).to_string(); + CString::new(output).unwrap().into_raw() + } + Ok(None) => std::ptr::null_mut(), + Err(e) => { + let output = serde_json::json!({"error": e}).to_string(); + CString::new(output).unwrap().into_raw() + } + } +} + +/// Set a hook function pointer and mask bit by name +/// +/// Hook names map to array indices: pre_build=0, post_convert=1, post_render=2, post_build=3 +pub fn __set_hook( + name: &str, + mask: &mut u32, + hooks: &mut [Option; 4], + func: PluginFn, +) { + match name { + "pre_build" => { + *mask |= HOOK_PRE_BUILD; + hooks[0] = Some(func); + } + "post_convert" => { + *mask |= HOOK_POST_CONVERT; + hooks[1] = Some(func); + } + "post_render" => { + *mask |= HOOK_POST_RENDER; + hooks[2] = Some(func); + } + "post_build" => { + *mask |= HOOK_POST_BUILD; + hooks[3] = Some(func); + } + _ => {} + } +} + +/// Register a plugin with the given name and version. +/// +/// Generates the `norgolith_plugin_init` function and bridge functions for each hook. +/// +/// # Example +/// +/// ```rust +/// use norgolith_plugin_sdk::*; +/// +/// fn highlight(json: serde_json::Value) -> Result, String> { +/// let ctx: TransformContext = serde_json::from_value(json).map_err(|e| e.to_string())?; +/// Ok(Some(ctx.html)) +/// } +/// +/// register_plugin!("my-plugin", "0.1.0", +/// hooks: [post_render: highlight] +/// ); +/// ``` +#[macro_export] +macro_rules! register_plugin { + ($name:expr, $version:expr, hooks: [$($hook:ident : $handler:ident),* $(,)?]) => { + // Generate one bridge function per hook + $( + #[no_mangle] + pub extern "C" fn $hook(input: *const ::std::os::raw::c_char) -> *mut ::std::os::raw::c_char { + $crate::__bridge_json(input, $handler) + } + )* + + // Generate the init function + #[no_mangle] + pub extern "C" fn norgolith_plugin_init( + info: &mut $crate::PluginInfo, + mask: &mut u32, + hooks: &mut [Option<$crate::PluginFn>; 4], + ) { + *info = $crate::PluginInfo { + abi_version: $crate::CORE_ABI_VERSION, + name: concat!($name, "\0").as_ptr() as *const ::std::os::raw::c_char, + version: concat!($version, "\0").as_ptr() as *const ::std::os::raw::c_char, + }; + *mask = 0; + *hooks = [None, None, None, None]; + $( + $crate::__set_hook(stringify!($hook), mask, hooks, $hook as $crate::PluginFn); + )* + } + }; +} + +#[cfg(test)] +mod tests { + use super::*; + use std::ffi::CString; + + #[test] + fn test_bridge_json_success() { + let input = CString::new(r#"{"html":"hello","metadata":{},"rel_path":"test.norg"}"#).unwrap(); + + fn handler(json: serde_json::Value) -> Result, String> { + let html = json.get("html").and_then(|v| v.as_str()).unwrap(); + Ok(Some(format!("{} world", html))) + } + + let result = __bridge_json(input.as_ptr(), handler); + assert!(!result.is_null()); + + let output = unsafe { CStr::from_ptr(result) }.to_str().unwrap(); + let parsed: serde_json::Value = serde_json::from_str(output).unwrap(); + assert_eq!(parsed.get("html").and_then(|v| v.as_str()).unwrap(), "hello world"); + } + + #[test] + fn test_bridge_json_no_change() { + let input = CString::new(r#"{"html":"keep","metadata":{},"rel_path":"test.norg"}"#).unwrap(); + + fn handler(_json: serde_json::Value) -> Result, String> { + Ok(None) + } + + let result = __bridge_json(input.as_ptr(), handler); + assert!(result.is_null()); + } + + #[test] + fn test_bridge_json_error() { + let input = CString::new(r#"{"html":"fail","metadata":{},"rel_path":"test.norg"}"#).unwrap(); + + fn handler(_json: serde_json::Value) -> Result, String> { + Err("something went wrong".to_string()) + } + + let result = __bridge_json(input.as_ptr(), handler); + assert!(!result.is_null()); + + let output = unsafe { CStr::from_ptr(result) }.to_str().unwrap(); + let parsed: serde_json::Value = serde_json::from_str(output).unwrap(); + assert_eq!(parsed.get("error").and_then(|v| v.as_str()).unwrap(), "something went wrong"); + } + + #[test] + fn test_set_hook_all_names() { + let mut mask = 0u32; + let mut hooks: [Option; 4] = [None, None, None, None]; + + extern "C" fn dummy(_input: *const c_char) -> *mut c_char { + std::ptr::null_mut() + } + + __set_hook("pre_build", &mut mask, &mut hooks, dummy); + assert_eq!(mask, HOOK_PRE_BUILD); + assert!(hooks[0].is_some()); + + __set_hook("post_convert", &mut mask, &mut hooks, dummy); + assert_eq!(mask, HOOK_PRE_BUILD | HOOK_POST_CONVERT); + assert!(hooks[1].is_some()); + + __set_hook("post_render", &mut mask, &mut hooks, dummy); + assert_eq!(mask, HOOK_PRE_BUILD | HOOK_POST_CONVERT | HOOK_POST_RENDER); + assert!(hooks[2].is_some()); + + __set_hook("post_build", &mut mask, &mut hooks, dummy); + assert_eq!(mask, HOOK_PRE_BUILD | HOOK_POST_CONVERT | HOOK_POST_RENDER | HOOK_POST_BUILD); + assert!(hooks[3].is_some()); + } + + #[test] + fn test_set_hook_unknown_name() { + let mut mask = 0u32; + let mut hooks: [Option; 4] = [None, None, None, None]; + + extern "C" fn dummy(_input: *const c_char) -> *mut c_char { + std::ptr::null_mut() + } + + __set_hook("unknown_hook", &mut mask, &mut hooks, dummy); + assert_eq!(mask, 0); + assert!(hooks.iter().all(|h| h.is_none())); + } +}