diff --git a/.gitignore b/.gitignore index b4e5e56..4093094 100644 --- a/.gitignore +++ b/.gitignore @@ -55,7 +55,6 @@ test/*.pem test/*.srl test/*.log test/_build_* -test/cpp-httplib/ work/ benchmark/server* docs-gen/target/ diff --git a/docs-gen/Cargo.lock b/docs-gen/Cargo.lock index 6d12b67..59b4c04 100644 --- a/docs-gen/Cargo.lock +++ b/docs-gen/Cargo.lock @@ -62,7 +62,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys", + "windows-sys 0.61.2", ] [[package]] @@ -73,7 +73,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys", + "windows-sys 0.61.2", ] [[package]] @@ -82,6 +82,12 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +[[package]] +name = "ascii" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" + [[package]] name = "autocfg" version = "1.5.0" @@ -103,6 +109,12 @@ dependencies = [ "serde", ] +[[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.0" @@ -134,6 +146,18 @@ version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +[[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.56" @@ -183,6 +207,12 @@ dependencies = [ "phf_codegen", ] +[[package]] +name = "chunked_transfer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4de3bc4ea267985becf712dc6d9eed8b04c953b3fcfb339ebc87acd9804901" + [[package]] name = "clap" version = "4.5.60" @@ -288,6 +318,12 @@ dependencies = [ "typenum", ] +[[package]] +name = "data-encoding" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" + [[package]] name = "deranged" version = "0.5.8" @@ -319,13 +355,19 @@ version = "0.1.0" dependencies = [ "anyhow", "clap", + "notify", + "open", "pulldown-cmark", "serde", "serde_json", "serde_yml", + "socket2", "syntect", + "tempfile", "tera", + "tiny_http", "toml", + "tungstenite", "walkdir", ] @@ -335,6 +377,33 @@ 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 = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "filetime" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db" +dependencies = [ + "cfg-if", + "libc", + "libredox", +] + [[package]] name = "find-msvc-tools" version = "0.1.9" @@ -357,6 +426,21 @@ 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 = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -387,6 +471,19 @@ dependencies = [ "wasi", ] +[[package]] +name = "getrandom" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", +] + [[package]] name = "globset" version = "0.4.18" @@ -406,11 +503,20 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" dependencies = [ - "bitflags", + "bitflags 2.11.0", "ignore", "walkdir", ] +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + [[package]] name = "hashbrown" version = "0.16.1" @@ -423,6 +529,28 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[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" @@ -456,6 +584,12 @@ dependencies = [ "cc", ] +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + [[package]] name = "ignore" version = "0.4.25" @@ -479,7 +613,57 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.16.1", + "serde", + "serde_core", +] + +[[package]] +name = "inotify" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd168d97690d0b8c412d6b6c10360277f4d7ee495c5d0d5d5fe0854923255cc" +dependencies = [ + "bitflags 1.3.2", + "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 = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + +[[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]] @@ -504,12 +688,38 @@ dependencies = [ "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.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + [[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.182" @@ -522,6 +732,18 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" +[[package]] +name = "libredox" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a" +dependencies = [ + "bitflags 2.11.0", + "libc", + "plain", + "redox_syscall", +] + [[package]] name = "libyml" version = "0.0.5" @@ -538,6 +760,12 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + [[package]] name = "log" version = "0.4.29" @@ -560,6 +788,46 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "mio" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "notify" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c533b4c39709f9ba5005d8002048266593c1cfaf3c5f0739d5b8ab0c6c504009" +dependencies = [ + "bitflags 2.11.0", + "filetime", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "log", + "mio", + "notify-types", + "walkdir", + "windows-sys 0.52.0", +] + +[[package]] +name = "notify-types" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585d3cb5e12e01aed9e8a1f70d5c6b5e86fe2a6e48fc8cd0b3e0b8df6f6eb174" +dependencies = [ + "instant", +] + [[package]] name = "num-conv" version = "0.2.0" @@ -593,7 +861,7 @@ version = "6.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "336b9c63443aceef14bea841b899035ae3abe89b7c486aaf4c5bd8aafedac3f0" dependencies = [ - "bitflags", + "bitflags 2.11.0", "libc", "once_cell", "onig_sys", @@ -609,6 +877,17 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "open" +version = "5.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43bb73a7fa3799b198970490a51174027ba0d4ec504b03cd08caf513d40024bc" +dependencies = [ + "is-wsl", + "libc", + "pathdiff", +] + [[package]] name = "parse-zoneinfo" version = "0.3.1" @@ -618,6 +897,12 @@ dependencies = [ "regex", ] +[[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" @@ -711,6 +996,12 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + [[package]] name = "plist" version = "1.8.0" @@ -739,6 +1030,16 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro2" version = "1.0.106" @@ -754,7 +1055,7 @@ version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f86ba2052aebccc42cbbb3ed234b8b13ce76f75c3551a303cb2bcffcff12bb14" dependencies = [ - "bitflags", + "bitflags 2.11.0", "getopts", "memchr", "pulldown-cmark-escape", @@ -785,6 +1086,12 @@ 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 = "rand" version = "0.8.5" @@ -812,7 +1119,16 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.17", +] + +[[package]] +name = "redox_syscall" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce70a74e890531977d37e532c34d45e9055d2409ed08ddba14529471ed0be16" +dependencies = [ + "bitflags 2.11.0", ] [[package]] @@ -844,6 +1160,19 @@ version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags 2.11.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + [[package]] name = "rustversion" version = "1.0.22" @@ -865,6 +1194,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + [[package]] name = "serde" version = "1.0.228" @@ -932,6 +1267,17 @@ dependencies = [ "version_check", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sha2" version = "0.10.9" @@ -971,6 +1317,16 @@ dependencies = [ "wasm-bindgen", ] +[[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 = "strsim" version = "0.11.1" @@ -1004,11 +1360,24 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "thiserror", + "thiserror 2.0.18", "walkdir", "yaml-rust", ] +[[package]] +name = "tempfile" +version = "3.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82a72c767771b47409d2345987fda8628641887d5466101319899796367354a0" +dependencies = [ + "fastrand", + "getrandom 0.4.1", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + [[package]] name = "tera" version = "1.20.1" @@ -1031,13 +1400,33 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + [[package]] name = "thiserror" version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl", + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -1082,6 +1471,18 @@ dependencies = [ "time-core", ] +[[package]] +name = "tiny_http" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389915df6413a2e74fb181895f933386023c71110878cd0825588928e64cdc82" +dependencies = [ + "ascii", + "chunked_transfer", + "httpdate", + "log", +] + [[package]] name = "toml" version = "0.8.23" @@ -1123,6 +1524,24 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" +[[package]] +name = "tungstenite" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand", + "sha1", + "thiserror 1.0.69", + "utf-8", +] + [[package]] name = "typenum" version = "1.19.0" @@ -1159,6 +1578,18 @@ 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 = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "utf8parse" version = "0.2.2" @@ -1187,6 +1618,24 @@ 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.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[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", +] + [[package]] name = "wasm-bindgen" version = "0.2.114" @@ -1232,13 +1681,47 @@ 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.0", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + [[package]] name = "winapi-util" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys", + "windows-sys 0.61.2", ] [[package]] @@ -1300,6 +1783,15 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.61.2" @@ -1309,6 +1801,70 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[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_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[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_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[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_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + [[package]] name = "winnow" version = "0.7.14" @@ -1318,6 +1874,94 @@ 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-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", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.11.0", + "indexmap", + "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 = "yaml-rust" version = "0.4.5" diff --git a/docs-gen/Cargo.toml b/docs-gen/Cargo.toml index 2e9cd03..1986d1d 100644 --- a/docs-gen/Cargo.toml +++ b/docs-gen/Cargo.toml @@ -14,3 +14,9 @@ toml = "0.8" syntect = "5" anyhow = "1" clap = { version = "4", features = ["derive"] } +notify = "7" +tiny_http = "0.12" +tungstenite = "0.24" +open = "5" +tempfile = "3" +socket2 = { version = "0.5", features = ["all"] } diff --git a/docs-gen/defaults/config.toml b/docs-gen/defaults/config.toml index c948b66..47a780d 100644 --- a/docs-gen/defaults/config.toml +++ b/docs-gen/defaults/config.toml @@ -3,6 +3,15 @@ title = "My Docs" base_url = "https://example.com" base_path = "" +# [[nav]] +# label = "Guide" +# path = "guide/" +# +# [[nav]] +# label = "GitHub" +# url = "https://github.com/your/repo" +# icon = "github" + [i18n] default_lang = "en" langs = ["en", "ja"] diff --git a/docs-gen/defaults/static/css/main.css b/docs-gen/defaults/static/css/main.css index 702b809..0f2ded0 100644 --- a/docs-gen/defaults/static/css/main.css +++ b/docs-gen/defaults/static/css/main.css @@ -86,14 +86,22 @@ a:hover { } .header-nav a { + display: flex; + align-items: center; + gap: 4px; color: var(--header-nav-link); font-size: 0.9rem; } +.header-nav a svg { + flex-shrink: 0; + opacity: 0.85; +} + .header-tools { display: flex; align-items: center; - gap: 8px; + gap: 2px; } .lang-selector { @@ -101,17 +109,21 @@ a:hover { } .lang-btn { + display: flex; + align-items: center; + gap: 5px; background: none; - border: 1px solid var(--text-muted); + border: none; color: var(--text); - padding: 4px 10px; + padding: 4px 6px; border-radius: 4px; cursor: pointer; font-size: 0.85rem; + opacity: 0.8; } .lang-btn:hover { - border-color: var(--text); + opacity: 1; } .lang-popup { @@ -423,16 +435,18 @@ a:hover { /* Theme toggle */ .theme-toggle { + display: flex; + align-items: center; + justify-content: center; background: none; - border: 1px solid var(--text-muted); + border: none; color: var(--text); - padding: 4px 8px; + padding: 5px 6px; border-radius: 4px; cursor: pointer; - font-size: 1rem; - line-height: 1; + opacity: 0.8; } .theme-toggle:hover { - border-color: var(--text); + opacity: 1; } diff --git a/docs-gen/defaults/static/favicon.svg b/docs-gen/defaults/static/favicon.svg new file mode 100644 index 0000000..9e824c2 --- /dev/null +++ b/docs-gen/defaults/static/favicon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/docs-gen/defaults/static/js/main.js b/docs-gen/defaults/static/js/main.js index 5ed3fbe..ae0034b 100644 --- a/docs-gen/defaults/static/js/main.js +++ b/docs-gen/defaults/static/js/main.js @@ -33,6 +33,10 @@ var btn = document.querySelector('.theme-toggle'); if (!btn) return; + // Feather Icons: sun (light mode) and moon (dark mode) + var sunSVG = ''; + var moonSVG = ''; + function getTheme() { var stored = localStorage.getItem('preferred-theme'); if (stored) return stored; @@ -45,7 +49,7 @@ } else { document.documentElement.removeAttribute('data-theme'); } - btn.textContent = theme === 'light' ? '\u2600\uFE0F' : '\uD83C\uDF19'; + btn.innerHTML = theme === 'light' ? sunSVG : moonSVG; } applyTheme(getTheme()); diff --git a/docs-gen/defaults/templates/base.html b/docs-gen/defaults/templates/base.html index afed676..153377b 100644 --- a/docs-gen/defaults/templates/base.html +++ b/docs-gen/defaults/templates/base.html @@ -4,6 +4,7 @@ {{ page.title }} - {{ site.title }} + "#; + +/// Run the serve command: build, start HTTP + WebSocket servers, watch for changes. +pub fn serve(src: &Path, port: u16, open_browser: bool) -> Result<()> { + let config = SiteConfig::load(src)?; + let base_path = config.site.base_path.clone(); + let ws_port = port + 1; + + // Create temp directory for serving + let tmp_dir = tempfile::tempdir().context("Failed to create temp directory")?; + let serve_root = tmp_dir.path().to_path_buf(); + + println!("Serving from temp directory: {}", serve_root.display()); + + // Initial build + build_and_copy(src, &serve_root, &base_path, ws_port)?; + + // Track connected WebSocket clients + let clients: Arc>> = Arc::new(Mutex::new(Vec::new())); + + // Create HTTP and WebSocket listeners upfront with SO_REUSEADDR + // so that rapid restarts (after Ctrl+C) don't hit "address in use". + let http_listener = create_reuse_listener(port) + .with_context(|| format!("Failed to bind HTTP server to port {}", port))?; + let ws_listener = create_reuse_listener(ws_port) + .with_context(|| format!("Failed to bind WebSocket server to port {}", ws_port))?; + + // Start WebSocket server for live-reload notifications + let ws_clients = clients.clone(); + thread::spawn(move || { + if let Err(e) = run_ws_server(ws_listener, ws_clients) { + eprintln!("WebSocket server error: {}", e); + } + }); + + // Start HTTP server + let http_root = serve_root.clone(); + thread::spawn(move || { + if let Err(e) = run_http_server(http_listener, &http_root) { + eprintln!("HTTP server error: {}", e); + } + }); + + let url = if base_path.is_empty() { + format!("http://localhost:{}/", port) + } else { + format!("http://localhost:{}{}/", port, base_path) + }; + + println!("\n Local: {}", url); + println!(" Press Ctrl+C to stop.\n"); + + if open_browser { + let _ = open::that(&url); + } + + // File watcher + let (tx, rx) = mpsc::channel(); + + let mut watcher = notify::recommended_watcher(move |res: Result| { + if let Ok(event) = res { + if event.kind.is_modify() || event.kind.is_create() || event.kind.is_remove() { + let _ = tx.send(()); + } + } + })?; + + let src_abs = fs::canonicalize(src)?; + watcher.watch(&src_abs, RecursiveMode::Recursive)?; + + println!("Watching for changes in {}...", src_abs.display()); + + // Debounce: wait for changes, then rebuild + loop { + // Block until a change notification arrives + if rx.recv().is_err() { + break; + } + // Drain any additional events within a short debounce window + thread::sleep(Duration::from_millis(200)); + while rx.try_recv().is_ok() {} + + println!("Change detected, rebuilding..."); + match build_and_copy(src, &serve_root, &base_path, ws_port) { + Ok(()) => { + println!("Rebuild complete. Notifying browser..."); + notify_clients(&clients); + } + Err(e) => { + eprintln!("Rebuild failed: {}", e); + } + } + } + + Ok(()) +} + +/// Build site into a temp build dir, then copy to serve_root// +/// with live-reload script injected. +fn build_and_copy(src: &Path, serve_root: &Path, base_path: &str, ws_port: u16) -> Result<()> { + // Build into a temporary output directory + let build_tmp = tempfile::tempdir().context("Failed to create build temp dir")?; + let build_out = build_tmp.path(); + + builder::build(src, build_out)?; + + // Determine the target directory under serve_root + let target = if base_path.is_empty() { + serve_root.to_path_buf() + } else { + let bp = base_path.trim_start_matches('/'); + serve_root.join(bp) + }; + + // Clean target and copy + if target.exists() { + fs::remove_dir_all(&target).ok(); + } + copy_dir_recursive(build_out, &target)?; + + // Inject live-reload script into all HTML files + inject_live_reload(&target, ws_port)?; + + Ok(()) +} + +/// Inject live-reload WebSocket script into all HTML files under dir. +fn inject_live_reload(dir: &Path, ws_port: u16) -> Result<()> { + let script = LIVE_RELOAD_SCRIPT.replace("{{WS_PORT}}", &ws_port.to_string()); + + for entry in WalkDir::new(dir) + .into_iter() + .filter_map(|e| e.ok()) + .filter(|e| { + e.path() + .extension() + .map_or(false, |ext| ext == "html") + }) + { + let path = entry.path(); + let content = fs::read_to_string(path)?; + if let Some(pos) = content.rfind("") { + let injected = format!("{}{}{}", &content[..pos], script, &content[pos..]); + fs::write(path, injected)?; + } + } + + Ok(()) +} + +/// Simple HTTP static file server using tiny_http. +fn run_http_server(listener: TcpListener, root: &Path) -> Result<()> { + let server = tiny_http::Server::from_listener(listener, None) + .map_err(|e| anyhow::anyhow!("HTTP server: {}", e))?; + + for request in server.incoming_requests() { + let url_path = percent_decode(request.url()); + let rel = url_path.trim_start_matches('/'); + + let file_path = if rel.is_empty() { + root.join("index.html") + } else { + let candidate = root.join(rel); + if candidate.is_dir() { + candidate.join("index.html") + } else { + candidate + } + }; + + if file_path.exists() && file_path.is_file() { + let content = fs::read(&file_path).unwrap_or_default(); + let mime = guess_mime(&file_path); + let response = tiny_http::Response::from_data(content) + .with_header( + tiny_http::Header::from_bytes(&b"Content-Type"[..], mime.as_bytes()).unwrap(), + ); + let _ = request.respond(response); + } else { + let response = tiny_http::Response::from_string("404 Not Found") + .with_status_code(404); + let _ = request.respond(response); + } + } + + Ok(()) +} + +/// WebSocket server that accepts connections and stores them for later notification. +fn run_ws_server(listener: TcpListener, clients: Arc>>) -> Result<()> { + + for stream in listener.incoming().flatten() { + let clients = clients.clone(); + thread::spawn(move || { + if let Ok(ws) = tungstenite::accept(stream.try_clone().unwrap()) { + // Store the underlying TCP stream for later notification + if let Ok(mut list) = clients.lock() { + list.push(stream); + } + // Keep the WebSocket connection alive - read until closed + let mut ws = ws; + loop { + match ws.read() { + Ok(msg) => { + if msg.is_close() { + break; + } + } + Err(_) => break, + } + } + } + }); + } + + Ok(()) +} + +/// Send "reload" to all connected WebSocket clients. +fn notify_clients(clients: &Arc>>) { + if let Ok(mut list) = clients.lock() { + let mut alive = Vec::new(); + for stream in list.drain(..) { + if stream.try_clone().is_ok() { + // Re-wrap as WebSocket and send reload message + // Since we can't easily re-wrap existing TCP streams, + // we'll use a simpler approach: raw WebSocket frame + if send_ws_text_frame(&stream, "reload").is_ok() { + alive.push(stream); + } + } + } + *list = alive; + } +} + +/// Send a WebSocket text frame directly on a TCP stream. +fn send_ws_text_frame(mut stream: &TcpStream, msg: &str) -> Result<()> { + let payload = msg.as_bytes(); + let len = payload.len(); + + // WebSocket text frame: opcode 0x81 + let mut frame = Vec::new(); + frame.push(0x81); + if len < 126 { + frame.push(len as u8); + } else if len < 65536 { + frame.push(126); + frame.push((len >> 8) as u8); + frame.push((len & 0xFF) as u8); + } + frame.extend_from_slice(payload); + + stream.write_all(&frame)?; + stream.flush()?; + Ok(()) +} + +fn copy_dir_recursive(src: &Path, dst: &Path) -> Result<()> { + for entry in WalkDir::new(src).into_iter().filter_map(|e| e.ok()) { + let path = entry.path(); + let rel = path.strip_prefix(src)?; + let target = dst.join(rel); + + if path.is_dir() { + fs::create_dir_all(&target)?; + } else { + if let Some(parent) = target.parent() { + fs::create_dir_all(parent)?; + } + fs::copy(path, &target)?; + } + } + Ok(()) +} + +fn guess_mime(path: &Path) -> String { + match path.extension().and_then(|e| e.to_str()) { + Some("html") => "text/html; charset=utf-8".to_string(), + Some("css") => "text/css; charset=utf-8".to_string(), + Some("js") => "application/javascript; charset=utf-8".to_string(), + Some("json") => "application/json; charset=utf-8".to_string(), + Some("svg") => "image/svg+xml".to_string(), + Some("png") => "image/png".to_string(), + Some("jpg") | Some("jpeg") => "image/jpeg".to_string(), + Some("gif") => "image/gif".to_string(), + Some("ico") => "image/x-icon".to_string(), + Some("wasm") => "application/wasm".to_string(), + Some("woff") => "font/woff".to_string(), + Some("woff2") => "font/woff2".to_string(), + Some("ttf") => "font/ttf".to_string(), + _ => "application/octet-stream".to_string(), + } +} + +fn percent_decode(input: &str) -> String { + let mut result = String::with_capacity(input.len()); + let mut chars = input.bytes(); + while let Some(b) = chars.next() { + if b == b'%' { + let hi = chars.next().and_then(|c| hex_val(c)); + let lo = chars.next().and_then(|c| hex_val(c)); + if let (Some(h), Some(l)) = (hi, lo) { + result.push((h << 4 | l) as char); + } + } else { + result.push(b as char); + } + } + result +} + +fn hex_val(b: u8) -> Option { + match b { + b'0'..=b'9' => Some(b - b'0'), + b'a'..=b'f' => Some(b - b'a' + 10), + b'A'..=b'F' => Some(b - b'A' + 10), + _ => None, + } +} + +/// Create a TCP listener with SO_REUSEADDR (and SO_REUSEPORT on Unix) set, +/// so that rapid restarts after Ctrl+C don't fail with "address in use". +fn create_reuse_listener(port: u16) -> Result { + let socket = Socket::new(Domain::IPV4, Type::STREAM, Some(Protocol::TCP))?; + socket.set_reuse_address(true)?; + #[cfg(unix)] + socket.set_reuse_port(true)?; + let addr: std::net::SocketAddr = format!("0.0.0.0:{}", port).parse()?; + socket.bind(&addr.into())?; + socket.listen(128)?; + Ok(socket.into()) +} diff --git a/docs-src/config.toml b/docs-src/config.toml index 5143d73..bffa7c2 100644 --- a/docs-src/config.toml +++ b/docs-src/config.toml @@ -6,6 +6,16 @@ base_url = "https://yhirose.github.io/cpp-httplib" # or "" for local development (python3 -m http.server). base_path = "/cpp-httplib" +[[nav]] +label = "Tour" +path = "tour/" +icon_svg = '' + +[[nav]] +label = "GitHub" +url = "https://github.com/yhirose/cpp-httplib" +icon_svg = '' + [i18n] default_lang = "en" langs = ["en", "ja"] diff --git a/docs/css/main.css b/docs/css/main.css index 702b809..edfa9a9 100644 --- a/docs/css/main.css +++ b/docs/css/main.css @@ -86,10 +86,18 @@ a:hover { } .header-nav a { + display: flex; + align-items: center; + gap: 4px; color: var(--header-nav-link); font-size: 0.9rem; } +.header-nav a svg { + flex-shrink: 0; + opacity: 0.85; +} + .header-tools { display: flex; align-items: center; @@ -101,17 +109,21 @@ a:hover { } .lang-btn { + display: flex; + align-items: center; + gap: 5px; background: none; - border: 1px solid var(--text-muted); + border: none; color: var(--text); - padding: 4px 10px; + padding: 4px 6px; border-radius: 4px; cursor: pointer; font-size: 0.85rem; + opacity: 0.8; } .lang-btn:hover { - border-color: var(--text); + opacity: 1; } .lang-popup { @@ -423,16 +435,18 @@ a:hover { /* Theme toggle */ .theme-toggle { + display: flex; + align-items: center; + justify-content: center; background: none; - border: 1px solid var(--text-muted); + border: none; color: var(--text); - padding: 4px 8px; + padding: 5px 6px; border-radius: 4px; cursor: pointer; - font-size: 1rem; - line-height: 1; + opacity: 0.8; } .theme-toggle:hover { - border-color: var(--text); + opacity: 1; } diff --git a/docs/en/cookbook/index.html b/docs/en/cookbook/index.html index fa919ad..47bd867 100644 --- a/docs/en/cookbook/index.html +++ b/docs/en/cookbook/index.html @@ -4,6 +4,7 @@ Cookbook - cpp-httplib +