From a907de8ff38d9eb6251ffde22e71d17fe9b3a9fb Mon Sep 17 00:00:00 2001 From: pyro Date: Wed, 20 May 2026 13:33:53 -0500 Subject: [PATCH] changed the cli to a tui environment. --- Cargo.lock | 1028 ++++++++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 2 + src/funcs.rs | 260 +++++++++--- src/install.rs | 4 + src/lib.rs | 295 ++++++++------ src/main.rs | 4 +- 6 files changed, 1376 insertions(+), 217 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bcf27fc..171792e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -32,6 +32,21 @@ dependencies = [ "zerocopy", ] +[[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-activity" version = "0.6.1" @@ -251,7 +266,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -286,7 +301,16 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", +] + +[[package]] +name = "atomic" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89cbf775b137e9b968e67227ef7f775587cde3fd31b0d8599dbd0f598a48340" +dependencies = [ + "bytemuck", ] [[package]] @@ -301,15 +325,36 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec 0.6.3", +] + [[package]] name = "bit-set" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" dependencies = [ - "bit-vec", + "bit-vec 0.8.0", ] +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + [[package]] name = "bit-vec" version = "0.8.0" @@ -334,6 +379,15 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" +[[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 = "block2" version = "0.5.1" @@ -388,7 +442,7 @@ checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -448,6 +502,15 @@ dependencies = [ "wayland-client", ] +[[package]] +name = "castaway" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" +dependencies = [ + "rustversion", +] + [[package]] name = "cc" version = "1.2.61" @@ -503,7 +566,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -578,6 +641,20 @@ dependencies = [ "memchr", ] +[[package]] +name = "compact_str" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb1325a1cece981e8a296ab8f0f9b63ae357bd0784a9faaf548cc7b480707a" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "rustversion", + "ryu", + "static_assertions", +] + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -607,6 +684,15 @@ dependencies = [ "tiny-keccak", ] +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -701,6 +787,15 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + [[package]] name = "crossbeam-deque" version = "0.8.6" @@ -726,6 +821,33 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[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", + "derive_more", + "document-features", + "mio", + "parking_lot", + "rustix 1.1.4", + "signal-hook", + "signal-hook-mio", + "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 = "crunchy" version = "0.2.4" @@ -745,6 +867,26 @@ dependencies = [ "wgpu", ] +[[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 = "csscolorparser" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb2a7d3066da2de787b7f032c736763eb7ae5d355f81a68bab2675a96008b0bf" +dependencies = [ + "lab", + "phf", +] + [[package]] name = "ctor" version = "0.10.1" @@ -760,6 +902,87 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.117", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "deltae" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5729f5117e208430e437df2f4843f5e5952997175992d1414f94c57d61e270b4" + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.117", +] + +[[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 = "dispatch" version = "0.2.0" @@ -842,7 +1065,7 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -907,18 +1130,57 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "fancy-regex" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b95f7c0680e4142284cf8b22c14a476e87d61b004a3a0861872b32ef7ead40a2" +dependencies = [ + "bit-set 0.5.3", + "regex", +] + [[package]] name = "fastrand" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" +[[package]] +name = "filedescriptor" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e40758ed24c9b2eeb76c35fb0aebc66c626084edd827e07e1552279814c6682d" +dependencies = [ + "libc", + "thiserror 1.0.69", + "winapi", +] + [[package]] name = "find-msvc-tools" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "finl_unicode" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9844ddc3a6e533d62bba727eb6c28b5d360921d5175e9ff0f1e621a5c590a4d5" + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[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" @@ -990,7 +1252,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1068,7 +1330,7 @@ checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1100,6 +1362,16 @@ dependencies = [ "slab", ] +[[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 = "gethostname" version = "1.1.0" @@ -1285,6 +1557,8 @@ version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ + "allocator-api2", + "equivalent", "foldhash 0.2.0", ] @@ -1509,6 +1783,12 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "indexmap" version = "2.14.0" @@ -1521,12 +1801,43 @@ dependencies = [ "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 = "instability" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eb2d60ef19920a3a9193c3e371f726ec1dafc045dac788d0fb3704272458971" +dependencies = [ + "darling", + "indoc", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[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.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.18" @@ -1560,7 +1871,7 @@ dependencies = [ "quote", "rustc_version", "simd_cesu8", - "syn", + "syn 2.0.117", ] [[package]] @@ -1588,7 +1899,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" dependencies = [ "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1613,6 +1924,17 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "kasuari" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bde5057d6143cc94e861d90f591b9303d6716c6b9602309150bd068853c10899" +dependencies = [ + "hashbrown 0.16.1", + "portable-atomic", + "thiserror 2.0.18", +] + [[package]] name = "khronos-egl" version = "6.0.0" @@ -1640,6 +1962,18 @@ dependencies = [ "smallvec", ] +[[package]] +name = "lab" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf36173d4167ed999940f804952e6b08197cae5ad5d572eb4db150ce8ad5d58f" + +[[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" @@ -1689,6 +2023,15 @@ dependencies = [ "web-time", ] +[[package]] +name = "line-clipping" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f50e8f47623268b5407192d26876c4d7f89d686ca130fdc53bced4814cd29f8" +dependencies = [ + "bitflags 2.11.1", +] + [[package]] name = "linebender_resource_handle" version = "0.1.1" @@ -1733,6 +2076,19 @@ name = "lru" version = "0.16.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39" +dependencies = [ + "hashbrown 0.16.1", +] + +[[package]] +name = "mac_address" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0aeb26bf5e836cc1c341c8106051b573f1766dfa05aa87f0b98be5e51b02303" +dependencies = [ + "nix", + "winapi", +] [[package]] name = "malloc_buf" @@ -1758,6 +2114,12 @@ dependencies = [ "libc", ] +[[package]] +name = "memmem" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a64a92489e2744ce060c349162be1c5f33c6969234104dbd99ddb5feb08b8c15" + [[package]] name = "memoffset" version = "0.9.1" @@ -1782,6 +2144,24 @@ dependencies = [ "paste", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[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 = "mundy" version = "0.2.3" @@ -1814,7 +2194,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "066cf25f0e8b11ee0df221219010f213ad429855f57c494f995590c861a9a7d8" dependencies = [ "arrayvec", - "bit-set", + "bit-set 0.8.0", "bitflags 2.11.1", "cfg-if", "cfg_aliases", @@ -1863,6 +2243,19 @@ dependencies = [ "jni-sys 0.3.1", ] +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.11.1", + "cfg-if", + "cfg_aliases", + "libc", + "memoffset", +] + [[package]] name = "no-std-compat" version = "0.4.1" @@ -1872,6 +2265,33 @@ dependencies = [ "spin", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -1901,7 +2321,16 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 2.0.117", +] + +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", ] [[package]] @@ -2288,6 +2717,15 @@ dependencies = [ "libredox", ] +[[package]] +name = "ordered-float" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" +dependencies = [ + "num-traits", +] + [[package]] name = "ordered-float" version = "5.3.0" @@ -2357,6 +2795,101 @@ 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", +] + +[[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" version = "1.1.12" @@ -2374,7 +2907,7 @@ checksum = "a990e22f43e84855daf260dded30524ef4a9021cc7541c26540500a50b624389" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2441,6 +2974,12 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "presser" version = "0.3.1" @@ -2454,7 +2993,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.117", ] [[package]] @@ -2511,6 +3050,21 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" +[[package]] +name = "rand" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + [[package]] name = "range-alloc" version = "0.1.5" @@ -2523,6 +3077,91 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "973443cf09a9c8656b574a866ab68dfa19f0867d0340648c7d2f6a71b8a8ea68" +[[package]] +name = "ratatui" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1ce67fb8ba4446454d1c8dbaeda0557ff5e94d39d5e5ed7f10a65eb4c8266bc" +dependencies = [ + "instability", + "ratatui-core", + "ratatui-crossterm", + "ratatui-macros", + "ratatui-termwiz", + "ratatui-widgets", +] + +[[package]] +name = "ratatui-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef8dea09a92caaf73bff7adb70b76162e5937524058a7e5bff37869cbbec293" +dependencies = [ + "bitflags 2.11.1", + "compact_str", + "hashbrown 0.16.1", + "indoc", + "itertools", + "kasuari", + "lru", + "strum", + "thiserror 2.0.18", + "unicode-segmentation", + "unicode-truncate", + "unicode-width", +] + +[[package]] +name = "ratatui-crossterm" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "577c9b9f652b4c121fb25c6a391dd06406d3b092ba68827e6d2f09550edc54b3" +dependencies = [ + "cfg-if", + "crossterm", + "instability", + "ratatui-core", +] + +[[package]] +name = "ratatui-macros" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7f1342a13e83e4bb9d0b793d0ea762be633f9582048c892ae9041ef39c936f4" +dependencies = [ + "ratatui-core", + "ratatui-widgets", +] + +[[package]] +name = "ratatui-termwiz" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f76fe0bd0ed4295f0321b1676732e2454024c15a35d01904ddb315afd3d545c" +dependencies = [ + "ratatui-core", + "termwiz", +] + +[[package]] +name = "ratatui-widgets" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7dbfa023cd4e604c2553483820c5fe8aa9d71a42eea5aa77c6e7f35756612db" +dependencies = [ + "bitflags 2.11.1", + "hashbrown 0.16.1", + "indoc", + "instability", + "itertools", + "line-clipping", + "ratatui-core", + "strum", + "time", + "unicode-segmentation", + "unicode-width", +] + [[package]] name = "raw-window-handle" version = "0.6.2" @@ -2597,6 +3236,35 @@ dependencies = [ "bitflags 2.11.1", ] +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + [[package]] name = "renderdoc-sys" version = "1.1.0" @@ -2631,7 +3299,7 @@ checksum = "d4322a2a4e8cf30771dd9f27f7f37ca9ac8fe812dddd811096a98483080dabe6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2693,6 +3361,12 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + [[package]] name = "same-file" version = "1.0.6" @@ -2766,7 +3440,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2790,7 +3464,18 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", ] [[package]] @@ -2799,6 +3484,27 @@ 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", + "signal-hook", +] + [[package]] name = "signal-hook-registry" version = "1.4.8" @@ -2825,6 +3531,12 @@ 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 = "skrifa" version = "0.37.0" @@ -3017,6 +3729,27 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "strum" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "svg_fmt" version = "0.4.5" @@ -3034,6 +3767,17 @@ dependencies = [ "zeno", ] +[[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" @@ -3076,12 +3820,77 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "terminfo" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4ea810f0692f9f51b382fff5893887bb4580f5fa246fde546e0b13e7fcee662" +dependencies = [ + "fnv", + "nom", + "phf", + "phf_codegen", +] + +[[package]] +name = "termios" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "411c5bf740737c7918b8b1fe232dca4dc9f8e754b8ad5e20966814001ed0ac6b" +dependencies = [ + "libc", +] + +[[package]] +name = "termwiz" +version = "0.23.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4676b37242ccbd1aabf56edb093a4827dc49086c0ffd764a5705899e0f35f8f7" +dependencies = [ + "anyhow", + "base64", + "bitflags 2.11.1", + "fancy-regex", + "filedescriptor", + "finl_unicode", + "fixedbitset", + "hex", + "lazy_static", + "libc", + "log", + "memmem", + "nix", + "num-derive", + "num-traits", + "ordered-float 4.6.0", + "pest", + "pest_derive", + "phf", + "sha2", + "signal-hook", + "siphasher", + "terminfo", + "termios", + "thiserror 1.0.69", + "ucd-trie", + "unicode-segmentation", + "vtparse", + "wezterm-bidi", + "wezterm-blob-leases", + "wezterm-color-types", + "wezterm-dynamic", + "wezterm-input-types", + "winapi", +] + [[package]] name = "tetanus" version = "0.1.0" dependencies = [ "clap", + "crossterm", "iced", + "ratatui", "rayon", "rhai", "rustc-hash 2.1.2", @@ -3123,7 +3932,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3134,9 +3943,30 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] +[[package]] +name = "time" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +dependencies = [ + "deranged", + "libc", + "num-conv", + "num_threads", + "powerfmt", + "serde_core", + "time-core", +] + +[[package]] +name = "time-core" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" + [[package]] name = "tiny-keccak" version = "2.0.2" @@ -3258,7 +4088,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3279,6 +4109,18 @@ dependencies = [ "core_maths", ] +[[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 = "uds_windows" version = "1.2.1" @@ -3320,6 +4162,17 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" +[[package]] +name = "unicode-truncate" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b380a1238663e5f8a691f9039c73e1cdae598a30e9855f541d29b08b53e9a5" +dependencies = [ + "itertools", + "unicode-segmentation", + "unicode-width", +] + [[package]] name = "unicode-width" version = "0.2.2" @@ -3344,6 +4197,8 @@ version = "1.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" dependencies = [ + "atomic", + "getrandom 0.4.2", "js-sys", "serde_core", "wasm-bindgen", @@ -3355,6 +4210,15 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "vtparse" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d9b2acfb050df409c972a37d3b8e08cdea3bddb0c09db9d53137e504cfabed0" +dependencies = [ + "utf8parse", +] + [[package]] name = "walkdir" version = "2.5.0" @@ -3431,7 +4295,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.117", "wasm-bindgen-shared", ] @@ -3647,6 +4511,78 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "wezterm-bidi" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0a6e355560527dd2d1cf7890652f4f09bb3433b6aadade4c9b5ed76de5f3ec" +dependencies = [ + "log", + "wezterm-dynamic", +] + +[[package]] +name = "wezterm-blob-leases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692daff6d93d94e29e4114544ef6d5c942a7ed998b37abdc19b17136ea428eb7" +dependencies = [ + "getrandom 0.3.4", + "mac_address", + "sha2", + "thiserror 1.0.69", + "uuid", +] + +[[package]] +name = "wezterm-color-types" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7de81ef35c9010270d63772bebef2f2d6d1f2d20a983d27505ac850b8c4b4296" +dependencies = [ + "csscolorparser", + "deltae", + "lazy_static", + "wezterm-dynamic", +] + +[[package]] +name = "wezterm-dynamic" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f2ab60e120fd6eaa68d9567f3226e876684639d22a4219b313ff69ec0ccd5ac" +dependencies = [ + "log", + "ordered-float 4.6.0", + "strsim", + "thiserror 1.0.69", + "wezterm-dynamic-derive", +] + +[[package]] +name = "wezterm-dynamic-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c0cf2d539c645b448eaffec9ec494b8b19bd5077d9e58cb1ae7efece8d575b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "wezterm-input-types" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7012add459f951456ec9d6c7e6fc340b1ce15d6fc9629f8c42853412c029e57e" +dependencies = [ + "bitflags 1.3.2", + "euclid", + "lazy_static", + "serde", + "wezterm-dynamic", +] + [[package]] name = "wgpu" version = "27.0.1" @@ -3683,8 +4619,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27a75de515543b1897b26119f93731b385a19aea165a1ec5f0e3acecc229cae7" dependencies = [ "arrayvec", - "bit-set", - "bit-vec", + "bit-set 0.8.0", + "bit-vec 0.8.0", "bitflags 2.11.1", "bytemuck", "cfg_aliases", @@ -3744,7 +4680,7 @@ dependencies = [ "android_system_properties", "arrayvec", "ash", - "bit-set", + "bit-set 0.8.0", "bitflags 2.11.1", "block", "bytemuck", @@ -3767,7 +4703,7 @@ dependencies = [ "ndk-sys", "objc", "once_cell", - "ordered-float", + "ordered-float 5.3.0", "parking_lot", "portable-atomic", "portable-atomic-util", @@ -3798,6 +4734,22 @@ dependencies = [ "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" @@ -3807,6 +4759,12 @@ 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 = "window_clipboard" version = "0.5.1" @@ -3897,7 +4855,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3908,7 +4866,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3919,7 +4877,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3930,7 +4888,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -4183,7 +5141,7 @@ dependencies = [ "heck", "indexmap", "prettyplease", - "syn", + "syn 2.0.117", "wasm-metadata", "wit-bindgen-core", "wit-component", @@ -4199,7 +5157,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn", + "syn 2.0.117", "wit-bindgen-core", "wit-bindgen-rust", ] @@ -4354,7 +5312,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 2.0.117", "zbus_names", "zvariant", "zvariant_utils", @@ -4394,7 +5352,7 @@ checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -4426,7 +5384,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 2.0.117", "zvariant_utils", ] @@ -4439,6 +5397,6 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn", + "syn 2.0.117", "winnow", ] diff --git a/Cargo.toml b/Cargo.toml index 1cce96b..0d9c6ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,9 @@ edition = "2024" [dependencies] clap = { version = "4.6.1", features = ["derive"] } +crossterm = "0.29.0" iced = { version = "0.14.0", features = ["advanced", "tokio"] } +ratatui = "0.30.0" rayon = "1.12.0" rhai = { version = "1.24.0", features = ["metadata", "sync"] } rustc-hash = "2.1.2" diff --git a/src/funcs.rs b/src/funcs.rs index ccbe401..7709dcc 100644 --- a/src/funcs.rs +++ b/src/funcs.rs @@ -1,7 +1,20 @@ use crate::*; +use crossterm::{ + cursor, + event::{ + self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEventKind, MouseEventKind, + }, + execute, + terminal::{EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode}, +}; +use ratatui::{ + prelude::*, + widgets::{Block, Borders, List, ListItem, ListState, Paragraph}, +}; use std::error::Error; -use std::io::Write; +use std::io::{Stdout, Write}; use std::thread; +use std::time::Duration; pub fn get_user_input(prompt: &str) -> Result> { println!("{}", prompt); @@ -10,76 +23,211 @@ pub fn get_user_input(prompt: &str) -> Result> { return Ok(response.trim().to_string()); } -pub fn cli(mut state: AppState, main_rx: Receiver) { - println!("Starting tetanus CLI..."); - let (input_tx, input_rx) = channel::(); - thread::spawn(move || { - let stdin = io::stdin(); - let mut input_buffer = String::new(); +pub fn run_tui( + mut state: AppState, + main_rx: Receiver, +) -> Result<(), Box> { + enable_raw_mode()?; + let mut stdout = io::stdout(); + execute!( + stdout, + EnterAlternateScreen, + cursor::Hide, + EnableMouseCapture + )?; + let backend = CrosstermBackend::new(stdout); + let mut terminal = Terminal::new(backend)?; + let (event_tx, event_rx) = channel::(); + let input_tx = event_tx.clone(); + std::thread::spawn(move || { loop { - input_buffer.clear(); - if stdin.read_line(&mut input_buffer).is_ok() { - let trimmed = input_buffer.trim().to_string(); - if !trimmed.is_empty() { - if input_tx.send(trimmed).is_err() { - break; + if event::poll(Duration::from_millis(100)).unwrap_or(false) { + match event::read() { + Ok(Event::Key(key)) => { + if key.kind == KeyEventKind::Press { + if input_tx.send(AppEvent::Key(key)).is_err() { + break; + } + } } + Ok(Event::Mouse(mouse)) => { + if input_tx.send(AppEvent::Mouse(mouse)).is_err() { + break; + } + } + _ => {} } } } }); - print!("tetanus> "); - let _ = io::stdout().flush(); - loop { - while let Ok(msg) = main_rx.try_recv() { - match msg { - ToolMessage::Output(txt) => { - println!("\n[result] {}", txt); - state.log.push(txt.clone()); - state.output.push(txt.clone()); - } - _ => {} + let worker_tx = event_tx.clone(); + std::thread::spawn(move || { + while let Ok(msg) = main_rx.recv() { + if worker_tx.send(AppEvent::Worker(msg)).is_err() { + break; } } - while let Ok(user_input) = input_rx.try_recv() { - state.curent_intput = user_input.clone(); - state.history.push(user_input.clone()); - match user_input.as_str() { - "exit" | "quit" => { - println!("shutting down..."); - return; + }); + let mut project_list_state = ListState::default(); + if !state.projects.is_empty() { + project_list_state.select(Some(0)); + } + loop { + terminal.draw(|f| { + let main_chunks = Layout::default() + .direction(Direction::Vertical) + .constraints([Constraint::Min(0), Constraint::Max(3)]) + .split(f.size()); + let top_chunks = Layout::default() + .direction(Direction::Horizontal) + .constraints([Constraint::Percentage(30), Constraint::Percentage(70)]) + .split(main_chunks[0]); + let projects: Vec = state + .projects + .iter() + .map(|p| ListItem::new(format!(" {} | {}", p.org_name, p.name))) + .collect(); + let projects_list = List::new(projects) + .block(Block::default().borders(Borders::ALL).title(" Projects ")) + .highlight_style( + Style::default() + .bg(Color::Blue) + .fg(Color::White) + .add_modifier(Modifier::BOLD), + ) + .highlight_symbol(">> "); + f.render_stateful_widget(projects_list, top_chunks[0], &mut project_list_state); + let output_lines: Vec = state + .output + .iter() + .map(|text| Line::from(Span::raw(text))) + .collect(); + let text_area_height = top_chunks[1].height.saturating_sub(2) as usize; + if state.output_scroll == u16::MAX { + if state.output.len() > text_area_height { + state.output_scroll = (state.output.len() - text_area_height) as u16; + } else { + state.output_scroll = 0; } - "reload-modules" => { - println!("reloadling modules..."); - state.initialize_modules(); - } - "help" => { - println!("\nAvailable Built-In & Custom Script Modules:"); - for (name, cmd) in &state.module_loader.commands { - println!(" - {:<15} (Outputs: {})", name, cmd.output_type); - if !cmd.help.is_empty() { - println!(" Help: {}", cmd.help.trim()); + } + let output_paragraph = Paragraph::new(output_lines) + .block( + Block::default() + .borders(Borders::ALL) + .title(" Script Engine Output "), + ) + .scroll((state.output_scroll, 0)) + .wrap(ratatui::widgets::Wrap { trim: false }); + + f.render_widget(output_paragraph, top_chunks[1]); + let input_paragraph = Paragraph::new(state.curent_intput.as_str()).block( + Block::default() + .borders(Borders::ALL) + .title(" Execute Command (Press Enter) "), + ); + + f.render_widget(input_paragraph, main_chunks[1]); + })?; + if let Ok(event) = event_rx.recv() { + match event { + AppEvent::Worker(msg) => match msg { + ToolMessage::Output(txt) => { + state.log.push(txt.clone()); + state.output.push(txt); + state.output_scroll = u16::MAX; + } + _ => {} + }, + AppEvent::Key(key) => match key.code { + KeyCode::Esc => break, + KeyCode::Enter => { + let trimmed = state.curent_intput.trim().to_string(); + if !trimmed.is_empty() { + state.history.push(trimmed.clone()); + state.output.push("\n".to_string()); + state.output.push(format!("[user input] > {}", trimmed)); + state.output.push("\n".to_string()); + let (command, args) = trimmed.split_once(' ').unwrap_or((&trimmed, "")); + match command { + "exit" | "quit" => break, + "reload-modules" => { + state.initialize_modules(); + state.output.push("Reloading module paths...".into()); + } + "help" => { + state.output.push("Available Modules:".into()); + for name in state.module_loader.commands.keys() { + state.output.push(format!(" - {}", name)); + } + } + "new_project" | "np" => { + if args.split_once(' ').is_some() { + let _ = + state.execute_command(command, Some(args.to_string())); + } else { + state.output.push("Error: USAGE -> np ".into()); + } + } + command_name => { + if state.module_loader.commands.contains_key(command_name) { + state.output.push(format!( + "[Worker] Executing script '{}'...", + command_name + )); + if let Err(e) = state.execute_command(command_name, None) { + state + .output + .push(format!("[Error] Pipeline fail: {}", e)); + } + } else { + state.output.push(format!( + "[Error] Command '{}' unknown.", + command_name + )); + } + } + } + state.curent_intput.clear(); } } - println!(); - } - // Everything else gets dynamically routed to your Rhai engine execution matrix! - command_name => { - if state.module_loader.commands.contains_key(command_name) { - println!( - "[Worker] Dispatching script '{}' to Rayon engine layer...", - command_name - ); - if let Err(e) = state.execute_command(command_name, None) { - eprintln!("[Error] Failed running script: {}", e); + KeyCode::Char(c) => { + state.curent_intput.push(c); + } + KeyCode::Backspace => { + state.curent_intput.pop(); + } + KeyCode::Up => { + if let Some(selected) = project_list_state.selected() { + if selected > 0 { + project_list_state.select(Some(selected - 1)); + } } - } else { - println!("[Error] Command '{}' unrecognized.", command_name); + } + KeyCode::Down => { + if let Some(selected) = project_list_state.selected() { + if selected + 1 < state.projects.len() { + project_list_state.select(Some(selected + 1)); + } + } + } + _ => {} + }, + AppEvent::Mouse(mouse) => { + if mouse.kind == crossterm::event::MouseEventKind::ScrollUp { + state.output_scroll = state.output_scroll.saturating_sub(1); + } else if mouse.kind == crossterm::event::MouseEventKind::ScrollDown { + state.output_scroll = state.output_scroll.saturating_add(1); } } } - print!("tetanus> "); - let _ = io::stdout().flush(); } } + disable_raw_mode()?; + execute!( + terminal.backend_mut(), + LeaveAlternateScreen, + cursor::Show, + DisableMouseCapture + )?; + Ok(()) } diff --git a/src/install.rs b/src/install.rs index 6b2de80..50a05a8 100644 --- a/src/install.rs +++ b/src/install.rs @@ -31,6 +31,9 @@ pub fn install() -> Result<(), Box> { let upcoming_notes_path = PathBuf::from(get_user_input( "enter the path to store upcoming project notes, or none if you want to be prompted everytime", )?); + let template_box = get_user_input( + "enter the name of your template distrobox, NONE in all caps if you are not using distrobox", + )?; config_path.push("client.conf"); projects_path.push("default"); create_dir_all(&projects_path)?; @@ -60,6 +63,7 @@ pub fn install() -> Result<(), Box> { client_config_file .write(format!("upcoming_notes: {}\n", upcoming_notes_path.display()).as_bytes())?; client_config_file.write(format!("module_path: {}\n", module_path.display()).as_bytes())?; + client_config_file.write(format!("template_box: {}\n", template_box).as_bytes())?; } else { eprintln!("error finding home directory!"); exit(1); diff --git a/src/lib.rs b/src/lib.rs index 7096c02..a399813 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,9 @@ +use ratatui::crossterm::event; use rhai::{AST, Dynamic, Engine, Scope}; use std::collections::HashMap; use std::error::Error; -use std::fs::{read_dir, read_to_string}; -use std::io; +use std::fs::{File, create_dir_all, read_dir, read_to_string}; +use std::io::{self, Write}; use std::path::PathBuf; use std::sync::mpsc::Receiver; use std::sync::{Arc, mpsc::Sender, mpsc::channel}; @@ -24,6 +25,7 @@ pub struct AppState { pub selected_project: Option, pub curent_intput: String, pub module_loader: ModuleLoader, + pub output_scroll: u16, } impl AppState { @@ -48,6 +50,7 @@ impl AppState { selected_project: None, curent_intput: String::new(), module_loader: ModuleLoader::new(), + output_scroll: 0, }, main_rx, ) @@ -114,118 +117,151 @@ impl AppState { eprintln!("Warning: 'module_path' is missing from AppState config map!"); } } - - /// Dispatches a command execution payload to the Rayon thread pool pub fn execute_command( - &self, + &mut self, command_name: &str, - raw_input_string: Option, + command_args: Option, ) -> Result<(), Box> { - // 1. Retrieve the compiled AST and Engine reference - let ast = self - .module_loader - .asts - .get(command_name) - .ok_or_else(|| format!("AST not found for command: {}", command_name))? - .clone(); + let tx = self.main_tx.clone(); + match command_name { + "new_project" | "np" => { + let args = command_args.unwrap(); + let (org, name) = args.split_once(" ").unwrap(); + if let Err(e) = self.new_project(org.to_string(), name.to_string()) { + tx.send(ToolMessage::Output(format!( + "Error making {}-{}: {e}", + org, name + )))?; + } else { + tx.send(ToolMessage::Output(format!( + "{}-{} created successfully!", + org, name + )))?; + } + } + _ => { + let ast = self + .module_loader + .asts + .get(command_name) + .ok_or_else(|| format!("AST not found for command: {}", command_name))? + .clone(); - let engine = Arc::clone(&self.module_loader.engine); - let cmd_meta = self - .module_loader - .commands - .get(command_name) - .ok_or_else(|| format!("Metadata not found for command: {}", command_name))? - .clone(); - - // 2. Build out the script Scope using values cloned from AppState - let mut scope = Scope::new(); - - // Dynamically add requested args into script context based on config.conf parsing rules - for arg_requirement in &cmd_meta.args { - match arg_requirement.as_str() { - "project" => { - if let Some(ref proj) = self.selected_project { - scope.push("project", proj.clone()); + let engine = Arc::clone(&self.module_loader.engine); + let cmd_meta = self + .module_loader + .commands + .get(command_name) + .ok_or_else(|| format!("Metadata not found for command: {}", command_name))? + .clone(); + let mut scope = Scope::new(); + for arg_requirement in &cmd_meta.args { + match arg_requirement.as_str() { + "project" => { + if let Some(ref proj) = self.selected_project { + scope.push("project", proj.clone()); + } + } + "projects" => { + let mut arr = rhai::Array::new(); + for proj in &self.projects { + arr.push(rhai::Dynamic::from(proj.clone())); + } + scope.push("projects", arr); + } + "host" => { + if let Some(host) = + self.selected_project.as_ref().and_then(|p| p.hosts.first()) + { + scope.push("host", host.clone()); + } + } + "hosts" => { + if let Some(ref proj) = self.selected_project { + scope.push("hosts", proj.hosts.clone()); + } + } + "config" => { + let mut map = rhai::Map::new(); + for (k, v) in &self.config { + map.insert(k.clone().into(), v.clone().into()); + } + scope.push("config", map); + } + _ if arg_requirement.starts_with("string") => { + let input_val = command_args + .clone() + .unwrap_or_else(|| self.curent_intput.clone()); + scope.push("input_string", input_val); + } + _ => {} } } - "projects" => { - let mut arr = rhai::Array::new(); - for proj in &self.projects { - arr.push(rhai::Dynamic::from(proj.clone())); + self.workers.spawn(move || { + match engine.eval_ast_with_scope::(&mut scope, &ast) { + Ok(result) => { + let result_str = if result.is_array() { + if let Ok(arr) = result.into_array() { + let lines: Vec = arr + .into_iter() + .map(|item| { + item.into_string().unwrap_or_else(|_| "".into()) + }) + .collect(); + lines.join("\n") + } else { + "failed to process array output".into() + } + } else if result.is_string() { + result + .into_string() + .unwrap_or_else(|_| "failed to parse string".into()) + } else { + format!("{:?}", result) + }; + if result_str.contains("\n") { + result_str.lines().into_iter().for_each(|line| { + let _ = tx.send(ToolMessage::Output(line.to_string())); + }); + } else { + let _ = tx.send(ToolMessage::Output(result_str)); + } + } + Err(err) => { + let _ = tx.send(ToolMessage::Output(format!( + "Script Error in execution: {}", + err + ))); + } } - scope.push("projects", arr); - } - "host" => { - if let Some(host) = self.selected_project.as_ref().and_then(|p| p.hosts.first()) - { - scope.push("host", host.clone()); - } - } - "hosts" => { - if let Some(ref proj) = self.selected_project { - scope.push("hosts", proj.hosts.clone()); - } - } - "config" => { - // Turn Hashmap into rhai::Map - let mut map = rhai::Map::new(); - for (k, v) in &self.config { - map.insert(k.clone().into(), v.clone().into()); - } - scope.push("config", map); - } - _ if arg_requirement.starts_with("string") => { - // If it asks for an input string, supply what the user provided or the current interactive line input - let input_val = raw_input_string - .clone() - .unwrap_or_else(|| self.curent_intput.clone()); - scope.push("input_string", input_val); - } - _ => {} + }); } } - - // 3. Clone our main communication MPSC tx channel to ship results back out of the Rayon worker threads - let tx = self.main_tx.clone(); - - // 4. Delegate to Rayon - self.workers.spawn(move || { - // This runs in a background thread inside Rayon! - match engine.eval_ast_with_scope::(&mut scope, &ast) { - Ok(result) => { - // Handle output based on the output type requested - let result_str = if result.is_array() { - if let Ok(arr) = result.into_array() { - let lines: Vec = arr - .into_iter() - .map(|item| item.into_string().unwrap_or_else(|_| "".into())) - .collect(); - lines.join("\n") - } else { - "failed to process array output".into() - } - } else if result.is_string() { - result - .into_string() - .unwrap_or_else(|_| "failed to parse string".into()) - } else { - format!("{:?}", result) - }; - let _ = tx.send(ToolMessage::Output(result_str)); - } - Err(err) => { - let _ = tx.send(ToolMessage::Output(format!( - "Script Error in execution: {}", - err - ))); - } - } - }); - Ok(()) } - pub fn new_project(&mut self) {} + pub fn new_project(&mut self, org: String, name: String) -> Result<(), Box> { + let template_box = self.config.get("template_box").unwrap(); + let project_files = PathBuf::from(self.config.get("upcoming_files").unwrap()) + .join(format!("{}/{}", org, name)); + let project_notes = PathBuf::from(self.config.get("upcoming_notes").unwrap()) + .join(format!("{}/{}", org, name)); + create_dir_all(&project_files)?; + create_dir_all(&project_notes)?; + let mut new_project = Project::new(); + let mut project_conf_folder = self.config_file.clone(); + project_conf_folder.pop(); + project_conf_folder.push(format!("projects/{}-{}", org, name)); + new_project.config_folder(project_conf_folder); + new_project.box_name(format!("{}-{}-{}", template_box, org, name)); + new_project.name = name; + new_project.org_name(org); + new_project.files(project_files); + new_project.notes(project_notes); + new_project.save_config()?; + self.projects.push(new_project); + return Ok(()); + } } #[derive(Clone)] @@ -243,6 +279,7 @@ pub struct Project { pub files: PathBuf, pub hosts: Vec, pub current: bool, + pub boxname: String, } impl Project { @@ -255,6 +292,7 @@ impl Project { files: PathBuf::new(), hosts: Vec::new(), current: false, + boxname: String::new(), } } @@ -282,6 +320,10 @@ impl Project { self.config_folder = path; } + pub fn box_name(&mut self, boxname: String) { + self.boxname = boxname; + } + pub fn load_config(&mut self) -> Result<(), Box> { let config_contents = read_to_string(&self.config_folder)?; for line in config_contents.lines() { @@ -304,6 +346,27 @@ impl Project { println!("{} | {} loaded!", self.org_name, self.name); return Ok(()); } + + pub fn save_config(&mut self) -> Result<(), Box> { + create_dir_all(&self.config_folder)?; + let mut conf_file = self.config_folder.clone(); + conf_file.push("project.conf"); + let mut file = File::create(conf_file)?; + let mut out_string = format!( + "org_name: {}\nname: {}\nnotes: {}\nfiles: {}\n", + self.org_name, + self.name, + self.notes.display(), + self.files.display() + ); + if self.current { + out_string.push_str("stage: current"); + } else { + out_string.push_str("stage: upcoming"); + } + file.write_all(out_string.as_bytes())?; + return Ok(()); + } } impl std::fmt::Display for Project { @@ -440,14 +503,12 @@ pub struct ToolCommand { pub struct ModuleLoader { pub engine: Arc, pub commands: HashMap, - // Stored separately because AST doesn't implement Debug/Clone easily pub asts: HashMap, } impl ModuleLoader { pub fn new() -> Self { let mut engine = Engine::new(); - // 1. Register PathBuf engine .register_type_with_name::("PathBuf") .register_get_set( @@ -455,8 +516,6 @@ impl ModuleLoader { |p: &mut PathBuf| p.to_string_lossy().into_owned(), |p: &mut PathBuf, s: String| *p = PathBuf::from(s), ); - - // 2. Register Host engine .register_type_with_name::("Host") .register_get_set( @@ -476,8 +535,6 @@ impl ModuleLoader { ) .register_fn("add_port", Host::add_port) .register_fn("add_finding", Host::add_finding); - - // 3. Register Project engine .register_type_with_name::("Project") .register_get_set( @@ -496,10 +553,6 @@ impl ModuleLoader { |p: &mut Project, val: bool| p.current = val, ) .register_fn("add_host", Project::add_host); - - // 4. Register custom Vector indexers / custom array conversion helpers - // Rhai works naturally with its own rhai::Array (Vec). - // To bridge Vec to Rhai scripts seamlessly, we can expose specialized utility methods. engine.register_fn("get_host", |hosts: &mut Vec, index: i64| { hosts.get(index as usize).cloned().unwrap_or_else(Host::new) }); @@ -511,7 +564,6 @@ impl ModuleLoader { } pub fn load_all(&mut self, base_path: &PathBuf) -> Result<(), std::io::Error> { - // Clear out existing maps in case this is called during a hot-reload self.commands.clear(); self.asts.clear(); @@ -532,7 +584,6 @@ impl ModuleLoader { if path.is_dir() { if let Some((command, ast)) = self.parse_module_directory(&path) { - // Cache both side-by-side using the parsed command name as the key self.asts.insert(command.name.clone(), ast); self.commands.insert(command.name.clone(), command); } @@ -549,13 +600,10 @@ impl ModuleLoader { if !config_path.exists() || !help_path.exists() || !script_path.exists() { return None; } - - // 1. Parse the config.conf file line by line let config_content = read_to_string(&config_path).ok()?; let mut name = String::new(); let mut output_type = String::new(); let mut args = Vec::new(); - for line in config_content.lines() { if let Some((key, val)) = line.split_once(':') { match key.trim() { @@ -572,15 +620,10 @@ impl ModuleLoader { } } } - if name.is_empty() { return None; } - - // 2. Read the help text let help = read_to_string(&help_path).unwrap_or_default(); - - // 3. Compile the Rhai script into an AST let ast = match self.engine.compile_file(script_path.clone()) { Ok(compiled_ast) => compiled_ast, Err(e) => { @@ -588,8 +631,6 @@ impl ModuleLoader { return None; } }; - - // 4. Assemble your exact ToolCommand struct let command = ToolCommand { name, path: script_path, @@ -603,3 +644,9 @@ impl ModuleLoader { Some((command, ast)) } } + +enum AppEvent { + Key(event::KeyEvent), + Worker(ToolMessage), + Mouse(event::MouseEvent), +} diff --git a/src/main.rs b/src/main.rs index 8c38d89..52fd61f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ use clap::Parser; use std::{env, path::PathBuf, process::exit}; use tetanus::AppState; -use tetanus::funcs::cli; +use tetanus::funcs::*; mod install; @@ -59,6 +59,6 @@ fn main() { exit(1); } appstate.initialize_modules(); - cli(appstate, rx); + run_tui(appstate, rx); } }