diff --git a/Cargo.lock b/Cargo.lock index 1032d62..e2bb0c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -164,15 +164,6 @@ version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" -[[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 = "bumpalo" version = "3.19.0" @@ -222,6 +213,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chdig" version = "25.11.2" @@ -249,8 +246,11 @@ dependencies = [ "pretty_assertions", "quick-xml", "ratatui", + "regex", + "reqwest", "semver", "serde", + "serde_json", "serde_yaml", "size", "strfmt", @@ -260,7 +260,6 @@ dependencies = [ "unicode-width 0.1.14", "url", "urlencoding", - "warp", ] [[package]] @@ -367,11 +366,11 @@ dependencies = [ "lz4", "percent-encoding", "pin-project", - "rustls", + "rustls 0.22.4", "rustls-pemfile", "thiserror 1.0.69", "tokio", - "tokio-rustls", + "tokio-rustls 0.25.0", "url", "uuid", "webpki-roots", @@ -421,15 +420,6 @@ 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 = "crc32fast" version = "1.5.0" @@ -521,16 +511,6 @@ 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 = "cursive" version = "0.21.1" @@ -695,16 +675,6 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" -[[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 = "displaydoc" version = "0.2.5" @@ -951,16 +921,6 @@ dependencies = [ "thread_local", ] -[[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.16" @@ -968,8 +928,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -979,9 +941,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi", "wasip2", + "wasm-bindgen", ] [[package]] @@ -996,25 +960,6 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" -[[package]] -name = "h2" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - [[package]] name = "hashbrown" version = "0.15.5" @@ -1032,30 +977,6 @@ version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" -[[package]] -name = "headers" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3314d5adb5d94bcdf56771f2e50dbbc80bb4bdf88967526706205ac9eff24eb" -dependencies = [ - "base64", - "bytes", - "headers-core", - "http", - "httpdate", - "mime", - "sha1", -] - -[[package]] -name = "headers-core" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" -dependencies = [ - "http", -] - [[package]] name = "heck" version = "0.5.0" @@ -1119,12 +1040,6 @@ 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 = "humantime" version = "2.3.0" @@ -1141,16 +1056,32 @@ dependencies = [ "bytes", "futures-channel", "futures-core", - "h2", "http", "http-body", "httparse", - "httpdate", "itoa", "pin-project-lite", "pin-utils", "smallvec", "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls 0.23.35", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.4", + "tower-service", + "webpki-roots", ] [[package]] @@ -1159,14 +1090,22 @@ version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52e9a2a24dc5c6821e71a7030e1e14b7b632acac55c40e9d2e082c621261bb56" dependencies = [ + "base64", "bytes", + "futures-channel", "futures-core", + "futures-util", "http", "http-body", "hyper", + "ipnet", + "libc", + "percent-encoding", "pin-project-lite", + "socket2", "tokio", "tower-service", + "tracing", ] [[package]] @@ -1333,6 +1272,22 @@ dependencies = [ "syn", ] +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "iri-string" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "is-terminal" version = "0.4.17" @@ -1438,6 +1393,12 @@ dependencies = [ "hashbrown 0.15.5", ] +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + [[package]] name = "lz4" version = "1.28.1" @@ -1469,22 +1430,6 @@ version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" -[[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 = "miniz_oxide" version = "0.8.9" @@ -1705,7 +1650,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ "phf_shared", - "rand", + "rand 0.8.5", ] [[package]] @@ -1783,6 +1728,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + [[package]] name = "pretty_assertions" version = "1.4.1" @@ -1812,6 +1766,61 @@ dependencies = [ "serde", ] +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls 0.23.35", + "socket2", + "thiserror 2.0.17", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +dependencies = [ + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand 0.9.2", + "ring", + "rustc-hash", + "rustls 0.23.35", + "rustls-pki-types", + "slab", + "thiserror 2.0.17", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.60.2", +] + [[package]] name = "quote" version = "1.0.42" @@ -1833,7 +1842,27 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core 0.9.3", +] + +[[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.3", ] [[package]] @@ -1842,6 +1871,15 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.4", +] + [[package]] name = "ratatui" version = "0.29.0" @@ -1901,6 +1939,44 @@ version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +[[package]] +name = "reqwest" +version = "0.12.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" +dependencies = [ + "base64", + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls 0.23.35", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls 0.26.4", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", +] + [[package]] name = "ring" version = "0.17.14" @@ -1921,6 +1997,12 @@ version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + [[package]] name = "rustix" version = "0.38.44" @@ -1956,7 +2038,21 @@ dependencies = [ "log", "ring", "rustls-pki-types", - "rustls-webpki", + "rustls-webpki 0.102.8", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls" +version = "0.23.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki 0.103.8", "subtle", "zeroize", ] @@ -1976,6 +2072,7 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" dependencies = [ + "web-time", "zeroize", ] @@ -1990,6 +2087,17 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustls-webpki" +version = "0.103.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.22" @@ -2011,12 +2119,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - [[package]] name = "scopeguard" version = "1.2.0" @@ -2097,17 +2199,6 @@ dependencies = [ "unsafe-libyaml", ] -[[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 = "shlex" version = "1.3.0" @@ -2247,6 +2338,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + [[package]] name = "synstructure" version = "0.13.2" @@ -2383,6 +2483,21 @@ dependencies = [ "zerovec", ] +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +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 = "tokio" version = "1.48.0" @@ -2415,24 +2530,60 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" dependencies = [ - "rustls", + "rustls 0.22.4", "rustls-pki-types", "tokio", ] [[package]] -name = "tokio-util" -version = "0.7.17" +name = "tokio-rustls" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls 0.23.35", + "tokio", +] + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ - "bytes", "futures-core", - "futures-sink", + "futures-util", "pin-project-lite", + "sync_wrapper", "tokio", + "tower-layer", + "tower-service", ] +[[package]] +name = "tower-http" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf146f99d442e8e68e585f5d798ccd3cad9a7835b917e09728880a862706456" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + [[package]] name = "tower-service" version = "0.3.3" @@ -2445,7 +2596,6 @@ version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ - "log", "pin-project-lite", "tracing-core", ] @@ -2459,6 +2609,12 @@ dependencies = [ "once_cell", ] +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + [[package]] name = "tui-input" version = "0.11.1" @@ -2469,18 +2625,6 @@ dependencies = [ "unicode-width 0.2.0", ] -[[package]] -name = "typenum" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" - -[[package]] -name = "unicase" -version = "2.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" - [[package]] name = "unicode-ident" version = "1.0.22" @@ -2585,32 +2729,12 @@ dependencies = [ ] [[package]] -name = "warp" -version = "0.4.2" +name = "want" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d06d9202adc1f15d709c4f4a2069be5428aa912cc025d6f268ac441ab066b0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "bytes", - "futures-util", - "headers", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-util", - "log", - "mime", - "mime_guess", - "percent-encoding", - "pin-project", - "scoped-tls", - "serde", - "serde_json", - "serde_urlencoded", - "tokio", - "tokio-util", - "tower-service", - "tracing", + "try-lock", ] [[package]] @@ -2641,6 +2765,19 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "551f88106c6d5e7ccc7cd9a16f312dd3b5d36ea8b4954304657d5dfba115d4a0" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.105" @@ -2673,6 +2810,26 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "web-sys" +version = "0.3.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "webpki-roots" version = "1.0.4" diff --git a/Cargo.toml b/Cargo.toml index 8ddfb6f..8c17efb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,7 +52,9 @@ serde_yaml = { version = "*", default-features = false } quick-xml = { version = "*", features = ["serialize"] } urlencoding = { version = "*", default-features = false } percent-encoding = { version = "*", default-features = false } -warp = { version = "*", default-features = false, features = ["server"] } +serde_json = { version = "*", default-features = false, features = ["std"] } +regex = { version = "*", default-features = false, features = ["std"] } +reqwest = { version = "*", default-features = false, features = ["rustls-tls"] } # CLI clap = { version = "*", default-features = false, features = ["derive", "env", "help", "usage", "std", "color", "error-context", "suggestions"] } clap_complete = { version = "*", default-features = false } diff --git a/src/interpreter/flamegraph.rs b/src/interpreter/flamegraph.rs index f60a917..eb6b621 100644 --- a/src/interpreter/flamegraph.rs +++ b/src/interpreter/flamegraph.rs @@ -6,15 +6,203 @@ use flamelens::app::{App, AppResult}; use flamelens::flame::FlameGraph; use flamelens::handler::handle_key_events; use flamelens::ui; -use futures::channel::mpsc; use ratatui::Terminal; use ratatui::backend::CrosstermBackend; +use regex::Regex; +use serde_json::json; use std::io; -use tokio::net::TcpListener; -use tokio::time::{Duration, sleep}; use urlencoding::encode; -use warp::Filter; -use warp::http::header::{HeaderMap, HeaderValue}; + +/// ClickHouse's SipHash-2-4 implementation (128-bit version) +/// See https://github.com/ClickHouse/ClickHouse/pull/46065 for details +struct ClickHouseSipHash { + v0: u64, + v1: u64, + v2: u64, + v3: u64, + cnt: u64, + current_word: u64, + current_bytes_len: usize, +} + +impl ClickHouseSipHash { + fn new() -> Self { + Self { + v0: 0x736f6d6570736575u64, + v1: 0x646f72616e646f6du64, + v2: 0x6c7967656e657261u64, + v3: 0x7465646279746573u64, + cnt: 0, + current_word: 0, + current_bytes_len: 0, + } + } + + #[inline] + fn sipround(&mut self) { + self.v0 = self.v0.wrapping_add(self.v1); + self.v1 = self.v1.rotate_left(13); + self.v1 ^= self.v0; + self.v0 = self.v0.rotate_left(32); + + self.v2 = self.v2.wrapping_add(self.v3); + self.v3 = self.v3.rotate_left(16); + self.v3 ^= self.v2; + + self.v0 = self.v0.wrapping_add(self.v3); + self.v3 = self.v3.rotate_left(21); + self.v3 ^= self.v0; + + self.v2 = self.v2.wrapping_add(self.v1); + self.v1 = self.v1.rotate_left(17); + self.v1 ^= self.v2; + self.v2 = self.v2.rotate_left(32); + } + + fn write(&mut self, data: &[u8]) { + for &byte in data { + let byte_idx = self.current_bytes_len; + self.current_word |= (byte as u64) << (byte_idx * 8); + self.current_bytes_len += 1; + self.cnt += 1; + + if self.current_bytes_len == 8 { + self.v3 ^= self.current_word; + self.sipround(); + self.sipround(); + self.v0 ^= self.current_word; + + self.current_word = 0; + self.current_bytes_len = 0; + } + } + } + + fn finish128(mut self) -> u128 { + // Set the last byte to cnt % 256 + let cnt_byte = (self.cnt % 256) as u8; + self.current_word |= (cnt_byte as u64) << 56; + + self.v3 ^= self.current_word; + self.sipround(); + self.sipround(); + self.v0 ^= self.current_word; + + // ClickHouse uses 0xff instead of 0xee + self.v2 ^= 0xff; + self.sipround(); + self.sipround(); + self.sipround(); + self.sipround(); + + // Combine v0, v1, v2, v3 into 128-bit result + let low = self.v0 ^ self.v1; + let high = self.v2 ^ self.v3; + + ((high as u128) << 64) | (low as u128) + } +} + +fn calculate_hash(text: &str) -> String { + let mut hasher = ClickHouseSipHash::new(); + hasher.write(text.as_bytes()); + let hash = hasher.finish128(); + format!("{:032x}", hash.swap_bytes()) +} + +fn get_fingerprint(text: &str) -> String { + let re = Regex::new(r"\b\w{4,100}\b").unwrap(); + let words: Vec<&str> = re.find_iter(text).map(|m| m.as_str()).collect(); + + if words.len() < 3 { + return "ffffffff".to_string(); + } + + let mut min_hash: Option = None; + + for i in 0..words.len().saturating_sub(2) { + let triplet = format!("{} {} {}", words[i], words[i + 1], words[i + 2]); + let mut hasher = ClickHouseSipHash::new(); + hasher.write(triplet.as_bytes()); + let hash_value = hasher.finish128(); + + min_hash = Some(min_hash.map_or(hash_value, |current| current.min(hash_value))); + } + + let full_hash = match min_hash { + Some(hash) => format!("{:032x}", hash.swap_bytes()), + None => "ffffffffffffffffffffffffffffffff".to_string(), + }; + full_hash[..8].to_string() +} + +async fn upload_to_pastila(content: &str) -> Result { + // FIXME: apparently the driver cannot work with async_insert, since the following does not + // work (simply hangs, since server expects more data) + // + // const PASTILA_HOST: &str = "uzg8q0g12h.eu-central-1.aws.clickhouse.cloud"; + // const PASTILA_USER: &str = "paste"; + // let fingerprint_hex = get_fingerprint(content); + // let hash_hex = calculate_hash(content); + // + // let options = Options::from_str(&format!( + // "tcp://{}@{}:9440/?secure=true&connection_timeout=5s", + // PASTILA_USER, PASTILA_HOST + // ))?; + // let pool = Pool::new(options); + // let mut client = pool.get_handle().await?; + // + // let block = Block::new() + // .column("fingerprint_hex", vec![fingerprint_hex.as_str()]) + // .column("hash_hex", vec![hash_hex.as_str()]) + // .column("content", vec![content]) + // .column("is_encrypted", vec![0_u8]); + // client.insert("paste.data", block).await?; + + const PASTILA_URL: &str = "https://uzg8q0g12h.eu-central-1.aws.clickhouse.cloud/?user=paste"; + let fingerprint_hex = get_fingerprint(content); + let hash_hex = calculate_hash(content); + + let json_data = json!({ + "fingerprint_hex": fingerprint_hex, + "hash_hex": hash_hex, + "content": content, + "is_encrypted": false + }); + + let insert_query = format!( + "INSERT INTO data (fingerprint_hex, hash_hex, content, is_encrypted) FORMAT JSONEachRow\n{}", + serde_json::to_string(&json_data)? + ); + + let client = reqwest::Client::new(); + let response = client + .post(PASTILA_URL) + .body(insert_query) + .send() + .await? + .error_for_status()?; + + // Note, this is not 100% guarantee due to async_insert. + if !response.status().is_success() { + return Err(Error::msg(format!( + "Failed to upload flamegraph data: {}", + response.status() + ))); + } + + let pastila_page_url = format!("https://pastila.nl/?{}/{}", fingerprint_hex, hash_hex); + log::info!("Pastila URL: {}", pastila_page_url); + + let select_query = format!( + "SELECT content FROM data_view(fingerprint = '{}', hash = '{}') FORMAT TabSeparatedRaw", + fingerprint_hex, hash_hex + ); + let clickhouse_url = format!("{}&query={}", PASTILA_URL, &select_query); + log::trace!("Pastila ClickHouse URL: {}", clickhouse_url); + + Ok(clickhouse_url) +} pub fn show(block: Columns) -> AppResult<()> { let data = block @@ -91,51 +279,25 @@ pub async fn open_in_speedscope(block: Columns) -> Result<()> { if data.trim().is_empty() { return Err(Error::msg("Flamegraph is empty")); - } else { - let (tx, mut rx) = mpsc::channel(1); - - let mut headers = HeaderMap::new(); - headers.insert("Access-Control-Allow-Origin", HeaderValue::from_static("*")); - - let route = warp::any() - .map(move || { - // stop the server - tx.clone().try_send(()).ok(); - return data.clone(); - }) - .with(warp::reply::with::headers(headers)); - - let listener = TcpListener::bind("127.0.0.1:0").await?; - let local_address = listener.local_addr()?; - let server = warp::serve(route).incoming(listener).graceful(async move { - while rx.try_next().is_err() { - sleep(Duration::from_millis(100)).await; - } - // FIXME: this is a dirty hack that assumes that 1 second is enough to server the - // request - sleep(Duration::from_secs(1)).await; - }); - - // NOTE: here we need a webserver, since we cannot use localProfilePath due to browser - // policies - let url = format!( - "https://www.speedscope.app/#profileURL={}", - encode(&format!("http://{}/", local_address)) - ); - let mut child = open_url_command(&url) - .spawn() - .map_err(|e| Error::msg(format!("Cannot open URL: {}", e)))?; - - let result = child.wait()?; - if !result.success() { - return Err(Error::msg(format!( - "Error while opening flamegraph in browser: {:?} (Do you have some browser installed?)", - result - ))); - } + } + + let pastila_url = upload_to_pastila(&data).await?; + + let url = format!( + "https://www.speedscope.app/#profileURL={}", + encode(&pastila_url) + ); + + let mut child = open_url_command(&url) + .spawn() + .map_err(|e| Error::msg(format!("Cannot open URL: {}", e)))?; - // TODO: correctly wait the server stopped serving - server.run().await; + let result = child.wait()?; + if !result.success() { + return Err(Error::msg(format!( + "Error while opening flamegraph in browser: {:?} (Do you have some browser installed?)", + result + ))); } return Ok(());