From 2a8a30fc14d0e31ab3a6a964dcbb1f672e9e2c40 Mon Sep 17 00:00:00 2001 From: Nikkuss Date: Sun, 15 Feb 2026 01:12:38 +0400 Subject: [PATCH] start work on tui app and rework secrets system --- .gitignore | 1 + Cargo.lock | 1256 +++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 17 + flake.lock | 39 +- flake.nix | 161 +++--- nix/flakeModule.nix | 80 ++- src/app.rs | 128 +++++ src/event.rs | 125 +++++ src/form.rs | 1 + src/main.rs | 41 ++ src/manifest.rs | 79 +++ src/ui.rs | 206 +++++++ 12 files changed, 2031 insertions(+), 103 deletions(-) create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/app.rs create mode 100644 src/event.rs create mode 100644 src/form.rs create mode 100644 src/main.rs create mode 100644 src/manifest.rs create mode 100644 src/ui.rs diff --git a/.gitignore b/.gitignore index fdf11bc..14c35b4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target /.direnv /result +/src-old diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..96d53af --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1256 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "addr2line" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[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 = "anstream" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "backtrace" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-link", +] + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "cassowary" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" + +[[package]] +name = "castaway" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" +dependencies = [ + "rustversion", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "clap" +version = "4.5.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6899ea499e3fb9305a65d5ebf6e3d2248c5fab291f300ad0a704fbe142eae31a" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b12c8b680195a62a8364d16b8447b01b6c2c8f9aaf68bee653be34d4245e238" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" + +[[package]] +name = "color-eyre" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5920befb47832a6d61ee3a3a846565cfa39b331331e68a3b1d1116630f2f26d" +dependencies = [ + "backtrace", + "color-spantrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", + "tracing-error", +] + +[[package]] +name = "color-spantrace" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8b88ea9df13354b55bc7234ebcce36e6ef896aca2e42a15de9e10edce01b427" +dependencies = [ + "once_cell", + "owo-colors", + "tracing-core", + "tracing-error", +] + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "compact_str" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "rustversion", + "ryu", + "static_assertions", +] + +[[package]] +name = "crossterm" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" +dependencies = [ + "bitflags", + "crossterm_winapi", + "futures-core", + "mio", + "parking_lot", + "rustix", + "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 = "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", +] + +[[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", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "gimli" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indenter" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" + +[[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.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357b7205c6cd18dd2c86ed312d1e70add149aea98e7ef72b9fdf0270e555c11d" +dependencies = [ + "darling", + "indoc", + "proc-macro2", + "quote", + "syn", +] + +[[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.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.180" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "lru" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", +] + +[[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 = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "owo-colors" +version = "4.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c6901729fa79e91a0913333229e9ca5dc725089d1c363b2f4b4760709dc4a52" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ratatui" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" +dependencies = [ + "bitflags", + "cassowary", + "compact_str", + "crossterm", + "indoc", + "instability", + "itertools", + "lru", + "paste", + "strum", + "unicode-segmentation", + "unicode-truncate", + "unicode-width 0.2.0", +] + +[[package]] +name = "ratatui-form" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a48928dba771cf305d19041c0671bb677ccdb3f33f1b0b19940acf44dfa1a62" +dependencies = [ + "crossterm", + "ratatui", + "regex", + "serde", + "serde_json", + "unicode-width 0.2.0", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[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.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" + +[[package]] +name = "rustc-demangle" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "sops-tui" +version = "0.1.0" +dependencies = [ + "clap", + "color-eyre", + "crossterm", + "futures", + "ratatui", + "ratatui-form", + "serde", + "serde_json", + "thiserror", + "tokio", + "unicode-width 0.2.0", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + +[[package]] +name = "syn" +version = "2.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "tokio" +version = "1.49.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-error" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db" +dependencies = [ + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" +dependencies = [ + "sharded-slab", + "thread_local", + "tracing-core", +] + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-truncate" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" +dependencies = [ + "itertools", + "unicode-segmentation", + "unicode-width 0.1.14", +] + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[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-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "zmij" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff05f8caa9038894637571ae6b9e29466c1f4f829d26c9b28f869a29cbe3445" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..25ebf2f --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "sops-tui" +version = "0.1.0" +edition = "2021" + +[dependencies] +ratatui = { version = "0.29", features = ["crossterm"] } +crossterm = { version = "0.28", features = ["event-stream"] } +tokio = { version = "1", features = ["full"] } +serde = { version = "1", features = ["derive"] } +serde_json = "1" +thiserror = "2" +clap = { version = "4", features = ["derive"] } +unicode-width = "0.2" +futures = "0.3" +color-eyre = "0.6.5" +ratatui-form = "0.1.1" diff --git a/flake.lock b/flake.lock index c9ffee9..6b85627 100644 --- a/flake.lock +++ b/flake.lock @@ -1,5 +1,20 @@ { "nodes": { + "crane": { + "locked": { + "lastModified": 1770169865, + "narHash": "sha256-iPiy13xzDQ9GjpOez+NNIjh/qjl7i4RDf9dF2x5mF9I=", + "owner": "ipetkov", + "repo": "crane", + "rev": "8254ccf3b5b5131890ee073776f2e61c6d1e55d4", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "repo": "crane", + "type": "github" + } + }, "flake-parts": { "inputs": { "nixpkgs-lib": "nixpkgs-lib" @@ -51,8 +66,30 @@ }, "root": { "inputs": { + "crane": "crane", "flake-parts": "flake-parts", - "nixpkgs": "nixpkgs" + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay" + } + }, + "rust-overlay": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1770347142, + "narHash": "sha256-uz+ZSqXpXEPtdRPYwvgsum/CfNq7AUQ/0gZHqTigiPM=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "2859683cd9ef7858d324c5399b0d8d6652bf4044", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" } } }, diff --git a/flake.nix b/flake.nix index a6f5730..150e232 100644 --- a/flake.nix +++ b/flake.nix @@ -4,86 +4,87 @@ inputs = { nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; flake-parts.url = "github:hercules-ci/flake-parts"; - - # crane.url = "github:ipetkov/crane"; - # flake-utils.url = "github:numtide/flake-utils"; - # rust-overlay = { - # url = "github:oxalica/rust-overlay"; - # inputs.nixpkgs.follows = "nixpkgs"; - # }; - }; - outputs = - { flake-parts, ... }@inputs: - flake-parts.lib.mkFlake { inherit inputs; } { - flake.flakeModules.default = ./nix/flakeModule.nix; + crane.url = "github:ipetkov/crane"; + rust-overlay = { + url = "github:oxalica/rust-overlay"; + inputs.nixpkgs.follows = "nixpkgs"; }; + }; - # outputs = - # { - # nixpkgs, - # crane, - # flake-utils, - # rust-overlay, - # ... - # }: - # flake-utils.lib.eachDefaultSystem ( - # system: - # let - # pkgs = import nixpkgs { - # inherit system; - # overlays = [ - # (import rust-overlay) - # ]; - # }; - # inherit (pkgs) lib; - # - # craneLib = (crane.mkLib pkgs).overrideToolchain ( - # p: - # p.rust-bin.nightly.latest.default.override { - # extensions = [ - # "rustc-codegen-cranelift-preview" - # "rust-analyzer" - # "rust-src" - # ]; - # } - # ); - # src = craneLib.cleanCargoSource ./.; - # commonArgs = { - # inherit src; - # strictDeps = true; - # }; - # cargoArtifacts = craneLib.buildDepsOnly commonArgs; - # individualCrateArgs = commonArgs // { - # inherit cargoArtifacts; - # inherit (craneLib.crateNameFromCargoToml { inherit src; }) version; - # }; - # - # fileSetForCrate = - # crate: - # lib.fileset.toSource { - # root = ./.; - # fileset = lib.fileset.unions [ - # ./Cargo.toml - # ./Cargo.lock - # ]; - # }; - # server = craneLib.buildPackage ( - # individualCrateArgs - # // { - # } - # ); - # in - # { - # devShells.default = craneLib.devShell { - # packages = with pkgs; [ - # mold - # llvmPackages.clang - # llvmPackages.lld - # sea-orm-cli - # watchexec - # pnpm - # ]; - # }; - # } - # ); + outputs = + { + flake-parts, + crane, + rust-overlay, + ... + }@inputs: + flake-parts.lib.mkFlake { inherit inputs; } { + systems = [ + "x86_64-linux" + "aarch64-linux" + "x86_64-darwin" + "aarch64-darwin" + ]; + flake.flakeModules.default = ./nix/flakeModule.nix; + perSystem = + { + pkgs, + lib, + system, + ... + }: + let + craneLib = (crane.mkLib pkgs).overrideToolchain ( + p: + p.rust-bin.nightly.latest.default.override { + extensions = [ + "rustc-codegen-cranelift-preview" + "rust-analyzer" + "rust-src" + ]; + } + ); + src = craneLib.cleanCargoSource ./.; + commonArgs = { + inherit src; + strictDeps = true; + }; + cargoArtifacts = craneLib.buildDepsOnly commonArgs; + individualCrateArgs = commonArgs // { + inherit cargoArtifacts; + inherit (craneLib.crateNameFromCargoToml { inherit src; }) version; + }; + + fileSetForCrate = + crate: + lib.fileset.toSource { + root = ./.; + fileset = lib.fileset.unions [ + ./Cargo.toml + ./Cargo.lock + ]; + }; + server = craneLib.buildPackage ( + individualCrateArgs + // { + } + ); + in + { + _module.args.pkgs = import inputs.nixpkgs { + inherit system; + overlays = [ inputs.rust-overlay.overlays.default ]; + }; + devShells.default = craneLib.devShell { + packages = with pkgs; [ + mold + llvmPackages.clang + llvmPackages.lld + sea-orm-cli + watchexec + pnpm + ]; + }; + }; + }; } diff --git a/nix/flakeModule.nix b/nix/flakeModule.nix index 8223611..18e4045 100644 --- a/nix/flakeModule.nix +++ b/nix/flakeModule.nix @@ -63,7 +63,7 @@ in secret_map = lib.mapAttrs ( name: value: let - keys = lib.unique ( + sopskeys = lib.unique ( lib.flatten (map (k: per_host_keys.${k}) value.hosts) ++ (lib.optionals value.global all_keys) ++ cfg.masterKeys @@ -76,35 +76,71 @@ in hosts global ; - inherit keys; + keys = value.keys or [ ]; + inherit sopskeys; } ) secrets; + rules = lib.mapAttrsToList (name: value: { path_regex = "${cfg.secretsDir}/${name}$"; - key_groups = [ { age = value.keys; } ]; + key_groups = [ { age = value.sopskeys; } ]; }) secret_map; - sops_secrets_map = lib.concatMapAttrs ( - name: value: - let - hasHost = (lib.elem "wawa" value.hosts) || value.global; - in - if hasHost then - { - ${name} = { - inherit (value) format neededForUsers; - sopsFile = inputs.self + "/${cfg.secretsDir}/${name}"; - }; - } - else - { } - ) secret_map; + sops_secrets_map = + host: + lib.mkMerge ( + lib.mapAttrsToList ( + name: value: + let + hasHost = (lib.elem host value.hosts) || value.global; + isYamlOrJson = value.format == "yaml" || value.format == "json"; + in + ( + ( + [ + ( + if hasHost && !(isYamlOrJson && value.keys != [ ]) then + { + ${name} = { + inherit (value) format neededForUsers; + sopsFile = inputs.self + "/${cfg.secretsDir}/${name}"; + }; + } + else + { } + ) + ] + ++ (lib.map (v: { + "${name}-${v}" = { + inherit (value) format neededForUsers; + sopsFile = inputs.self + "/${cfg.secretsDir}/${name}"; + key = v; + }; + }) value.keys) + ) + ) + ) secret_map + ); in { - secrets.nixosModule = - { ... }: - { - config.sops.secrets = sops_secrets_map; + flake.secretsManifest = { + secretsDir = cfg.secretsDir; + masterKeys = cfg.masterKeys; + hosts = lib.mapAttrs (_: keys: { inherit keys; }) per_host_keys; + secrets = secret_map; + secrets_map = sops_secrets_map; + }; + secrets.nixosModule = { + default = host: { + config = { + sops.secrets = sops_secrets_map host; + }; }; + network = + { config, ... }: + { + config.sops.secrets = sops_secrets_map config.networking.hostName; + }; + }; perSystem = { pkgs, self', ... }: let diff --git a/src/app.rs b/src/app.rs new file mode 100644 index 0000000..66ec476 --- /dev/null +++ b/src/app.rs @@ -0,0 +1,128 @@ +use color_eyre::Report; +use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; +use ratatui::{prelude::CrosstermBackend, widgets::ListState, DefaultTerminal, Terminal}; +use ratatui_form::{Email, Form}; +use std::{io, path::Path}; + +use crate::{ + event::{AppEvent, Event, EventHandler}, + form::FormState, + manifest::{load_manifest, Manifest}, +}; + +// #[derive(Debug)] +pub struct App { + pub running: bool, + pub list_state: ListState, + pub events: EventHandler, + pub isloadingmanifest: bool, + pub manifest: Option, +} + +impl App { + pub fn new() -> Self { + Self { + running: true, + list_state: ListState::default().with_selected(Some(0)), + events: EventHandler::new(), + isloadingmanifest: false, + manifest: None, + } + } + + pub async fn run( + mut self, + terminal: &mut Terminal>, + ) -> color_eyre::Result<()> { + let project_root = Path::new("/home/nikkuss/dotfiles-new"); + self.events.send(AppEvent::LoadManifest); + + while self.running { + terminal.draw(|frame| frame.render_widget(&mut self, frame.area()))?; + match self.events.next().await? { + Event::Tick => self.tick(), + Event::Crossterm(event) => match event { + crossterm::event::Event::Key(key_event) + if key_event.kind == crossterm::event::KeyEventKind::Press => + { + self.handle_key_events(key_event)? + } + _ => {} + }, + Event::App(app_event) => match app_event { + AppEvent::LoadManifest => { + if !self.isloadingmanifest { + let tx = self.events.clone_sender(); + tokio::spawn(async move { + let result = load_manifest(&project_root).await; + let _ = tx.send(Event::App(AppEvent::ManifestLoaded(result))); + }); + } + } + AppEvent::ManifestLoaded(manifest) => { + self.isloadingmanifest = false; + match manifest { + Ok(m) => { + self.manifest = Some(m); + } + Err(e) => { + self.display_error(e); + } + } + } + AppEvent::SelectionUp => self.selection_up(), + AppEvent::SelectionDown => self.selection_down(), + AppEvent::Quit => self.quit(), + }, + } + } + Ok(()) + } + + /// Handles the key events and updates the state of [`App`]. + pub fn handle_key_events(&mut self, key_event: KeyEvent) -> color_eyre::Result<()> { + match key_event.code { + KeyCode::Esc | KeyCode::Char('q') => self.events.send(AppEvent::Quit), + KeyCode::Char('c' | 'C') if key_event.modifiers == KeyModifiers::CONTROL => { + self.events.send(AppEvent::Quit) + } + KeyCode::Up => self.events.send(AppEvent::SelectionUp), + KeyCode::Down => self.events.send(AppEvent::SelectionDown), + KeyCode::Char('a') => self.create_secret(), + // Other handlers you could add here. + _ => {} + } + Ok(()) + } + + pub fn selected_secret_name(&self) -> Option { + let selected = self.list_state.selected()?; + if let Some(manifest) = &self.manifest { + return manifest.secrets.keys().nth(selected).cloned(); + } + None + } + + /// Handles the tick event of the terminal. + /// + /// The tick event is where you can update the state of your application with any logic that + /// needs to be updated at a fixed frame rate. E.g. polling a server, updating an animation. + pub fn tick(&self) {} + + /// Set running to false to quit the application. + pub fn quit(&mut self) { + self.running = false; + } + + pub fn selection_down(&mut self) { + self.list_state.select_next(); + } + + pub fn selection_up(&mut self) { + self.list_state.select_previous(); + } + pub fn display_error(&mut self, display_error: Report) { + // somehow display the error? + } + pub fn create_secret(&mut self) {} +} diff --git a/src/event.rs b/src/event.rs new file mode 100644 index 0000000..90f1a46 --- /dev/null +++ b/src/event.rs @@ -0,0 +1,125 @@ +use color_eyre::eyre::OptionExt; +use color_eyre::Result; +use futures::{FutureExt, StreamExt}; +use ratatui::crossterm::event::Event as CrosstermEvent; +use std::time::Duration; +use tokio::sync::mpsc::{self, UnboundedSender}; + +use crate::manifest::Manifest; + +/// The frequency at which tick events are emitted. +const TICK_FPS: f64 = 30.0; + +/// Representation of all possible events. +#[derive(Debug)] +pub enum Event { + Tick, + Crossterm(CrosstermEvent), + App(AppEvent), +} + +/// Application events. +/// +/// You can extend this enum with your own custom events. +#[derive(Debug)] +pub enum AppEvent { + SelectionUp, + SelectionDown, + LoadManifest, + ManifestLoaded(Result), + /// Quit the application. + Quit, +} + +/// Terminal event handler. +#[derive(Debug)] +pub struct EventHandler { + /// Event sender channel. + sender: mpsc::UnboundedSender, + /// Event receiver channel. + receiver: mpsc::UnboundedReceiver, +} + +impl EventHandler { + /// Constructs a new instance of [`EventHandler`] and spawns a new thread to handle events. + pub fn new() -> Self { + let (sender, receiver) = mpsc::unbounded_channel(); + let actor = EventTask::new(sender.clone()); + tokio::spawn(async { actor.run().await }); + Self { sender, receiver } + } + + /// Receives an event from the sender. + /// + /// This function blocks until an event is received. + /// + /// # Errors + /// + /// This function returns an error if the sender channel is disconnected. This can happen if an + /// error occurs in the event thread. In practice, this should not happen unless there is a + /// problem with the underlying terminal. + pub async fn next(&mut self) -> color_eyre::Result { + self.receiver + .recv() + .await + .ok_or_eyre("Failed to receive event") + } + + /// Queue an app event to be sent to the event receiver. + /// + /// This is useful for sending events to the event handler which will be processed by the next + /// iteration of the application's event loop. + pub fn send(&mut self, app_event: AppEvent) { + // Ignore the result as the reciever cannot be dropped while this struct still has a + // reference to it + let _ = self.sender.send(Event::App(app_event)); + } + pub fn clone_sender(&self) -> UnboundedSender { + self.sender.clone() + } +} + +/// A thread that handles reading crossterm events and emitting tick events on a regular schedule. +struct EventTask { + /// Event sender channel. + sender: mpsc::UnboundedSender, +} + +impl EventTask { + /// Constructs a new instance of [`EventThread`]. + fn new(sender: mpsc::UnboundedSender) -> Self { + Self { sender } + } + + /// Runs the event thread. + /// + /// This function emits tick events at a fixed rate and polls for crossterm events in between. + async fn run(self) -> color_eyre::Result<()> { + let tick_rate = Duration::from_secs_f64(1.0 / TICK_FPS); + let mut reader = crossterm::event::EventStream::new(); + let mut tick = tokio::time::interval(tick_rate); + loop { + let tick_delay = tick.tick(); + let crossterm_event = reader.next().fuse(); + tokio::select! { + _ = self.sender.closed() => { + break; + } + _ = tick_delay => { + self.send(Event::Tick); + } + Some(Ok(evt)) = crossterm_event => { + self.send(Event::Crossterm(evt)); + } + }; + } + Ok(()) + } + + /// Sends an event to the receiver. + fn send(&self, event: Event) { + // Ignores the result because shutting down the app drops the receiver, which causes the send + // operation to fail. This is expected behavior and should not panic. + let _ = self.sender.send(event); + } +} diff --git a/src/form.rs b/src/form.rs new file mode 100644 index 0000000..6481cc7 --- /dev/null +++ b/src/form.rs @@ -0,0 +1 @@ +pub struct FormState {} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..9be2d3e --- /dev/null +++ b/src/main.rs @@ -0,0 +1,41 @@ +use std::{io, path::Path}; + +use color_eyre::Result; +use crossterm::{ + execute, + terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, +}; +use ratatui::{prelude::CrosstermBackend, Terminal}; + +use crate::app::App; + +mod app; +mod event; +mod form; +mod manifest; +mod ui; + +#[tokio::main] +async fn main() -> Result<()> { + enable_raw_mode()?; + let mut stdout = io::stdout(); + execute!(stdout, EnterAlternateScreen)?; + let backend = CrosstermBackend::new(stdout); + let mut terminal = Terminal::new(backend)?; + + // Set up panic hook to restore terminal + let original_hook = std::panic::take_hook(); + std::panic::set_hook(Box::new(move |panic| { + let _ = disable_raw_mode(); + let _ = execute!(io::stdout(), LeaveAlternateScreen); + original_hook(panic); + })); + + // Run the app + let result = App::new().run(&mut terminal).await; + // Restore terminal + disable_raw_mode()?; + execute!(terminal.backend_mut(), LeaveAlternateScreen)?; + + result +} diff --git a/src/manifest.rs b/src/manifest.rs new file mode 100644 index 0000000..b9ebc1e --- /dev/null +++ b/src/manifest.rs @@ -0,0 +1,79 @@ +use color_eyre::{eyre::eyre, Result}; +use serde::Deserialize; +use std::{collections::BTreeMap, fmt, path::Path}; +use tokio::process::Command; + +/// Top-level manifest from `nix eval .#secretsManifest --json` +#[derive(Debug, Clone, Deserialize)] +pub struct Manifest { + #[serde(rename = "secretsDir")] + pub secrets_dir: String, + #[serde(rename = "masterKeys")] + pub master_keys: Vec, + pub hosts: BTreeMap, + pub secrets: BTreeMap, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct HostInfo { + pub keys: Vec, +} + +/// Per-secret info, pre-computed by Nix +#[derive(Debug, Clone, Deserialize)] +pub struct SecretInfo { + pub format: SecretFormat, + pub global: bool, + pub hosts: Vec, + #[serde(rename = "neededForUsers")] + pub needed_for_users: bool, + pub keys: Vec, +} + +#[derive(Debug, Clone, Deserialize, PartialEq)] +#[serde(rename_all = "lowercase")] +pub enum SecretFormat { + Json, + Yaml, + Dotenv, + Ini, + Binary, +} + +impl fmt::Display for SecretFormat { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + SecretFormat::Json => write!(f, "json"), + SecretFormat::Yaml => write!(f, "yaml"), + SecretFormat::Dotenv => write!(f, "dotenv"), + SecretFormat::Ini => write!(f, "ini"), + SecretFormat::Binary => write!(f, "binary"), + } + } +} + +/// Load manifest by running `nix eval .#secretsManifest --json` +pub async fn load_manifest(path: &Path) -> Result { + let path = path.to_string_lossy(); + let timeout_duration = std::time::Duration::from_secs(30); + let result = tokio::time::timeout(timeout_duration, async { + Command::new("nix") + .args(["eval", &format!("{path}#secretsManifest"), "--json"]) + .output() + .await + }) + .await; + + match result { + Ok(Ok(output)) => { + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + Err(eyre!("nix eval failed:\n{}", stderr)) + } else { + serde_json::from_slice(&output.stdout).map_err(Into::into) + } + } + Ok(Err(e)) => Err(eyre!("Failed to run nix eval: {}", e)), + Err(_) => Err(eyre!("nix eval timed out after 30s")), + } +} diff --git a/src/ui.rs b/src/ui.rs new file mode 100644 index 0000000..19826c0 --- /dev/null +++ b/src/ui.rs @@ -0,0 +1,206 @@ +use ratatui::{ + buffer::Buffer, + layout::{Alignment, Constraint, Direction, Layout, Rect}, + style::{Color, Modifier, Style, Stylize}, + text::{Line, Span, Text}, + widgets::{Block, BorderType, Borders, List, Paragraph, StatefulWidget, Widget}, +}; + +use crate::app::App; + +impl Widget for &mut App { + /// Renders the user interface widgets. + /// + // This is where you add new widgets. + // See the following resources: + // - https://docs.rs/ratatui/latest/ratatui/widgets/index.html + // - https://github.com/ratatui/ratatui/tree/master/examples + fn render(self, area: Rect, buf: &mut Buffer) { + let items = if let Some(manifest) = &self.manifest { + manifest + .secrets + .keys() + .map(|v| Text::from(truncate_with_ellipsis(v, 50))) + .collect() + } else { + Vec::new() + }; + let layout = Layout::default() + .direction(Direction::Horizontal) + .constraints([Constraint::Ratio(1, 3), Constraint::Ratio(2, 3)]) + .split(area); + let left = layout[0]; + let right = layout[1]; + let left_block = Block::default().title_bottom("meow").borders(Borders::ALL); + + let list = List::default() + .block(left_block) + .highlight_style(Style::default().bg(Color::Blue).fg(Color::White)) + .highlight_symbol("\u{25b6} ") + .items(items); + StatefulWidget::render(list, left, buf, &mut self.list_state); + + render_secret_detail(self, right, buf); + } +} + +fn render_secret_detail(state: &App, area: Rect, buf: &mut Buffer) { + let manifest = match &state.manifest { + Some(m) => m, + None => { + render_placeholder(area, buf, " Detail ", "No manifest loaded"); + return; + } + }; + + let selected_name = match state.selected_secret_name() { + Some(name) => name.to_string(), + None => { + render_placeholder(area, buf, " Detail ", "No secret selected"); + return; + } + }; + + let secret = match manifest.secrets.get(&selected_name) { + Some(s) => s, + None => return, + }; + + let title = format!(" {} ", selected_name); + let block = Block::default().title(title).borders(Borders::ALL); + // Build detail lines + let mut lines: Vec = Vec::new(); + + // Separator + lines.push(Line::from("")); + + // Format + lines.push(Line::from(vec![ + Span::styled(" Format: ", Style::default().fg(Color::DarkGray)), + Span::styled(secret.format.to_string(), Style::default().fg(Color::White)), + ])); + + // Scope + let scope = if secret.global { + "global".to_string() + } else { + "host-specific".to_string() + }; + lines.push(Line::from(vec![ + Span::styled(" Scope: ", Style::default().fg(Color::DarkGray)), + Span::styled(scope, Style::default().fg(Color::White)), + ])); + if !secret.global { + // Hosts + let hosts_str = if secret.hosts.is_empty() { + if secret.global { + manifest + .hosts + .keys() + .cloned() + .collect::>() + .join(", ") + } else { + "none".to_string() + } + } else { + secret.hosts.join(", ") + }; + lines.push(Line::from(vec![ + Span::styled(" Hosts: ", Style::default().fg(Color::DarkGray)), + Span::styled(hosts_str, Style::default().fg(Color::White)), + ])); + } + + // Recipients + lines.push(Line::from(vec![ + Span::styled(" Recipients: ", Style::default().fg(Color::DarkGray)), + Span::styled( + format!("{} age keys", secret.keys.len()), + Style::default().fg(Color::White), + ), + ])); + + // Needed for users + if secret.needed_for_users { + lines.push(Line::from(vec![ + Span::styled(" Users: ", Style::default().fg(Color::DarkGray)), + Span::styled("yes", Style::default().fg(Color::Yellow)), + ])); + } + while lines.len() < 6 { + lines.push(Line::from("")); + } + + // Key details section + lines.push(Line::from("")); + lines.push(Line::from(Span::styled( + " Keys:", + Style::default() + .fg(Color::DarkGray) + .add_modifier(Modifier::BOLD), + ))); + for key in &secret.keys { + let truncated = if key.len() > 20 { + format!("{}...", &key[..20]) + } else { + key.clone() + }; + + // Check if this is a master key + let label = if manifest.master_keys.contains(key) { + Span::styled(" (master)", Style::default().fg(Color::Magenta)) + } else { + // Try to find which host owns this key + let host_label = manifest + .hosts + .iter() + .find(|(_, info)| info.keys.contains(key)) + .map(|(name, _)| format!(" ({})", name)) + .unwrap_or_default(); + Span::styled(host_label, Style::default().fg(Color::Cyan)) + }; + + lines.push(Line::from(vec![ + Span::raw(" "), + Span::styled(truncated, Style::default().fg(Color::Green)), + label, + ])); + } + + let paragraph = Paragraph::new(lines).block(block).render(area, buf); +} + +fn render_placeholder(area: Rect, buf: &mut Buffer, title: &str, message: &str) { + let block = Block::default() + .title(title.to_string()) + .borders(Borders::ALL); + Paragraph::new(message.to_string()) + .style(Style::default().fg(Color::DarkGray)) + .block(block) + .render(area, buf); +} + +/// Truncate a name with middle ellipsis, preserving the file extension +fn truncate_with_ellipsis(name: &str, max_width: usize) -> String { + if name.len() <= max_width { + return name.to_string(); + } + + if max_width < 5 { + return name[..max_width].to_string(); + } + + // Try to preserve the extension + if let Some(dot_pos) = name.rfind('.') { + let ext = &name[dot_pos..]; + if ext.len() < max_width - 3 { + let prefix_len = max_width - 3 - ext.len(); + return format!("{}...{}", &name[..prefix_len], ext); + } + } + + // Fallback: just truncate with ellipsis at end + let prefix_len = max_width - 3; + format!("{}...", &name[..prefix_len]) +}