From 6423baebd92e45c9be85157c443dff42e65d8d14 Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Fri, 1 Sep 2023 00:00:00 +0000 Subject: refactor: rebuild app skeleton Create skeleton for a complete rebuild of the prototype as a production ready product. Includes design patterns for: - dependency injection - unit testing with dependency mocking - integration testing - error handling - config storage BREAKING-CHANGE: ground-up redesign with incompatible protocol standards --- .envrc | 1 + .github/workflows/check_test_rustfmt_clippy.yaml | 40 + .github/workflows/release.yml | 45 + .gitignore | 3 +- .vscode/settings.json | 6 + Cargo.lock | 1620 ++++++---------------- Cargo.toml | 28 +- LICENSE.md | 21 + README.md | 3 + flake.lock | 130 ++ flake.nix | 52 + git_hooks/commit-msg | 35 + git_hooks/pre-commit | 24 + rustfmt.toml | 13 + shell.nix | 1 + src/cli_interactor.rs | 34 + src/config.rs | 152 ++ src/key_handling/mod.rs | 1 + src/key_handling/users.rs | 124 ++ src/login.rs | 16 + src/main.rs | 35 + src/sub_commands/login.rs | 11 + src/sub_commands/mod.rs | 1 + test_utils/Cargo.toml | 12 + test_utils/src/lib.rs | 280 ++++ tests/login.rs | 145 ++ 26 files changed, 1598 insertions(+), 1235 deletions(-) create mode 100644 .envrc create mode 100644 .github/workflows/check_test_rustfmt_clippy.yaml create mode 100644 .github/workflows/release.yml create mode 100644 .vscode/settings.json create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 flake.lock create mode 100644 flake.nix create mode 100755 git_hooks/commit-msg create mode 100755 git_hooks/pre-commit create mode 100644 rustfmt.toml create mode 100644 shell.nix create mode 100644 src/cli_interactor.rs create mode 100644 src/config.rs create mode 100644 src/key_handling/mod.rs create mode 100644 src/key_handling/users.rs create mode 100644 src/login.rs create mode 100644 src/main.rs create mode 100644 src/sub_commands/login.rs create mode 100644 src/sub_commands/mod.rs create mode 100644 test_utils/Cargo.toml create mode 100644 test_utils/src/lib.rs create mode 100644 tests/login.rs diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..8392d15 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake \ No newline at end of file diff --git a/.github/workflows/check_test_rustfmt_clippy.yaml b/.github/workflows/check_test_rustfmt_clippy.yaml new file mode 100644 index 0000000..9cb63c1 --- /dev/null +++ b/.github/workflows/check_test_rustfmt_clippy.yaml @@ -0,0 +1,40 @@ +on: push + +name: check test rustfmt clippy + +jobs: + ci: + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - stable + - nightly + + steps: + - uses: actions/checkout@v2 + + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ matrix.rust }} + override: true + components: rustfmt, clippy + + - uses: actions-rs/cargo@v1 + with: + command: build + + - uses: actions-rs/cargo@v1 + with: + command: test + + - uses: actions-rs/cargo@v1 + with: + command: fmt + args: --all -- --check + + - uses: actions-rs/cargo@v1 + with: + command: clippy + args: -- -D warnings diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..7e64456 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,45 @@ +name: Release + +permissions: + contents: write + +on: + push: + tags: + - v[0-9]+.* + +jobs: + create-release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: taiki-e/create-gh-release-action@v1 + with: + # (required) GitHub token for creating GitHub Releases. + token: ${{ secrets.GITHUB_TOKEN }} + + upload-assets: + strategy: + matrix: + os: + - ubuntu-latest + - macos-latest + - windows-latest + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v3 + - uses: taiki-e/upload-rust-binary-action@v1 + with: + # (required) Comma-separated list of binary names (non-extension portion of filename) to build and upload. + # Note that glob pattern is not supported yet. + bin: ngit + # (optional) On which platform to distribute the `.tar.gz` file. + # [default value: unix] + # [possible values: all, unix, windows, none] + tar: unix + # (optional) On which platform to distribute the `.zip` file. + # [default value: windows] + # [possible values: all, unix, windows, none] + zip: windows + # (required) GitHub token for uploading assets to GitHub Releases. + token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 58c050e..cb8c9e0 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -.ngit +/target +.direnv \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..17edf5d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "[rust]": { + "editor.defaultFormatter": "rust-lang.rust-analyzer", + }, + "rust-analyzer.check.command": "clippy", +} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index a37e592..3471bc3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,42 +3,39 @@ version = 3 [[package]] -name = "aes" -version = "0.8.2" +name = "aho-corasick" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241" +checksum = "0c378d78423fdad8089616f827526ee33c19f2fddbd5de1629152c9593ba4783" dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", + "memchr", ] [[package]] name = "anstream" -version = "0.3.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6342bd4f5a1205d7f41e94a41a901f5647c938cdfa96036338e8533c9d6c2450" +checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", - "is-terminal", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" +checksum = "15c4c2c83f81532e5845a733998b6971faca23490340a418e9b72a3ec9de12ea" [[package]] name = "anstyle-parse" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" dependencies = [ "utf8parse", ] @@ -54,94 +51,40 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "1.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd" dependencies = [ "anstyle", "windows-sys 0.48.0", ] [[package]] -name = "async_io_stream" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6d7b9decdf35d8908a7e3ef02f64c5e9b1695e230154c0e8de3969142d9b94c" -dependencies = [ - "futures", - "pharos", - "rustc_version", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" - -[[package]] -name = "bech32" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" - -[[package]] -name = "bip39" -version = "2.0.0" +name = "anyhow" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f2635620bf0b9d4576eb7bb9a38a55df78bd1205d26fa994b25911a69f212f" -dependencies = [ - "bitcoin_hashes 0.11.0", - "serde", - "unicode-normalization", -] +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] -name = "bitcoin" -version = "0.30.0" +name = "assert_cmd" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b36f4c848f6bd9ff208128f08751135846cc23ae57d66ab10a22efff1c675f3c" +checksum = "88903cb14723e4d4003335bb7f8a14f27691649105346a0f0957466c096adfe6" dependencies = [ - "bech32", - "bitcoin-private", - "bitcoin_hashes 0.12.0", - "hex_lit", - "secp256k1", + "anstyle", + "bstr", + "doc-comment", + "predicates 3.0.3", + "predicates-core", + "predicates-tree", + "wait-timeout", ] [[package]] -name = "bitcoin-private" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73290177011694f38ec25e165d0387ab7ea749a4b81cd4c80dae5988229f7a57" - -[[package]] -name = "bitcoin_hashes" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90064b8dee6815a6470d60bad07bbbaee885c0e12d04177138fa3291a01b7bc4" - -[[package]] -name = "bitcoin_hashes" -version = "0.12.0" +name = "autocfg" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d7066118b13d4b20b23645932dfb3a81ce7e29f95726c2036fa33cd7b092501" -dependencies = [ - "bitcoin-private", - "serde", -] +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bitflags" @@ -150,57 +93,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[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 = "block-padding" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" -dependencies = [ - "generic-array", -] - -[[package]] -name = "bumpalo" -version = "3.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "1.4.0" +name = "bitflags" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" [[package]] -name = "cbc" -version = "0.1.2" +name = "bstr" +version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +checksum = "4c2f7349907b712260e64b0afe2f84692af14a454be26187d9df565c7f69266a" dependencies = [ - "cipher", + "memchr", + "regex-automata", + "serde", ] [[package]] name = "cc" -version = "1.0.79" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" dependencies = [ - "jobserver", + "libc", ] [[package]] @@ -209,57 +124,45 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", -] - [[package]] name = "clap" -version = "4.2.5" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a1f23fa97e1d1641371b51f35535cb26959b8e27ab50d167a8b996b5bada819" +checksum = "6a13b88d2c62ff462f88e4a121f17a82c1af05693a2f192b5c38d14de73c19f6" dependencies = [ "clap_builder", "clap_derive", - "once_cell", ] [[package]] name = "clap_builder" -version = "4.2.5" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fdc5d93c358224b4d6867ef1356d740de2303e9892edc06c5340daeccd96bab" +checksum = "2bb9faaa7c2ef94b2743a21f5a29e6f0010dff4caa69ac8e9d6cf8b6fa74da08" dependencies = [ "anstream", "anstyle", - "bitflags", "clap_lex", "strsim", ] [[package]] name = "clap_derive" -version = "4.2.0" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" +checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.32", ] [[package]] name = "clap_lex" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" +checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" [[package]] name = "colorchoice" @@ -268,47 +171,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] -name = "confy" -version = "0.5.1" +name = "comma" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e37668cb35145dcfaa1931a5f37fde375eeae8068b4c0d2f289da28a270b2d2c" -dependencies = [ - "directories", - "serde", - "thiserror", - "toml", -] +checksum = "55b672471b4e9f9e95499ea597ff64941a309b2cdbffcc46f2cc5e2d971fd335" [[package]] name = "console" -version = "0.15.5" +version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d79fbe8970a77e3e34151cc13d3b3e248aa0faaecb9f6091fa07ebefe5ad60" +checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" dependencies = [ "encode_unicode", "lazy_static", "libc", "unicode-width", - "windows-sys 0.42.0", -] - -[[package]] -name = "cpufeatures" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" -dependencies = [ - "libc", + "windows-sys 0.45.0", ] [[package]] -name = "crypto-common" -version = "0.1.6" +name = "dashmap" +version = "5.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ - "generic-array", - "typenum", + "cfg-if", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core", ] [[package]] @@ -324,61 +215,71 @@ dependencies = [ ] [[package]] -name = "digest" -version = "0.10.6" +name = "difflib" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" -dependencies = [ - "block-buffer", - "crypto-common", -] +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" [[package]] name = "directories" -version = "4.0.1" +version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" dependencies = [ "dirs-sys", ] [[package]] name = "dirs-sys" -version = "0.3.7" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" dependencies = [ "libc", + "option-ext", "redox_users", - "winapi", + "windows-sys 0.48.0", ] [[package]] -name = "either" -version = "1.8.1" +name = "doc-comment" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] -name = "encode_unicode" -version = "0.3.6" +name = "downcast" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" [[package]] -name = "encoding_rs" -version = "0.8.32" +name = "duplicate" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +checksum = "de78e66ac9061e030587b2a2e75cc88f22304913c907b11307bca737141230cb" dependencies = [ - "cfg-if", + "heck", + "proc-macro-error", ] +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + [[package]] name = "errno" -version = "0.3.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" dependencies = [ "errno-dragonfly", "libc", @@ -397,27 +298,24 @@ dependencies = [ [[package]] name = "fastrand" -version = "1.9.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" [[package]] -name = "fnv" -version = "1.0.7" +name = "float-cmp" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +dependencies = [ + "num-traits", +] [[package]] -name = "form_urlencoded" -version = "1.1.0" +name = "fragile" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" -dependencies = [ - "percent-encoding", -] +checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" [[package]] name = "futures" @@ -467,17 +365,6 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" -[[package]] -name = "futures-macro" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.15", -] - [[package]] name = "futures-sink" version = "0.3.28" @@ -499,7 +386,6 @@ dependencies = [ "futures-channel", "futures-core", "futures-io", - "futures-macro", "futures-sink", "futures-task", "memchr", @@ -508,600 +394,303 @@ 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 = "getrandom" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", - "js-sys", "libc", "wasi", - "wasm-bindgen", ] [[package]] -name = "git2" -version = "0.17.1" +name = "hashbrown" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b7905cdfe33d31a88bb2e8419ddd054451f5432d1da9eaf2ac7804ee1ea12d5" -dependencies = [ - "bitflags", - "libc", - "libgit2-sys", - "log", - "openssl-probe", - "openssl-sys", - "url", -] +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" [[package]] -name = "gloo-timers" -version = "0.2.6" +name = "heck" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] -name = "h2" -version = "0.3.18" +name = "itertools" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", + "either", ] [[package]] -name = "hashbrown" -version = "0.12.3" +name = "itoa" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] -name = "heck" -version = "0.4.1" +name = "lazy_static" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "linux-raw-sys" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" [[package]] -name = "hermit-abi" -version = "0.2.6" +name = "lock_api" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" dependencies = [ - "libc", + "autocfg", + "scopeguard", ] [[package]] -name = "hermit-abi" -version = "0.3.1" +name = "log" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] -name = "hex_lit" -version = "0.1.1" +name = "memchr" +version = "2.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" +checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" [[package]] -name = "http" -version = "0.2.9" +name = "memoffset" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" dependencies = [ - "bytes", - "fnv", - "itoa", + "autocfg", ] [[package]] -name = "http-body" -version = "0.4.5" +name = "mockall" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +checksum = "4c84490118f2ee2d74570d114f3d0493cbf02790df303d2707606c3e14e07c96" dependencies = [ - "bytes", - "http", - "pin-project-lite", + "cfg-if", + "downcast", + "fragile", + "lazy_static", + "mockall_derive", + "predicates 2.1.5", + "predicates-tree", ] [[package]] -name = "httparse" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" - -[[package]] -name = "httpdate" -version = "1.0.2" +name = "mockall_derive" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn 1.0.109", +] [[package]] -name = "hyper" -version = "0.14.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" +name = "ngit" +version = "0.0.1" dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", + "anyhow", + "assert_cmd", + "clap", + "dialoguer", + "directories", + "duplicate", + "mockall", + "serde", + "serde_json", + "serial_test", + "test_utils", ] [[package]] -name = "hyper-rustls" -version = "0.23.2" +name = "nix" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" dependencies = [ - "http", - "hyper", - "rustls", - "tokio", - "tokio-rustls", + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset", + "pin-utils", ] [[package]] -name = "idna" +name = "normalize-line-endings" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" [[package]] -name = "indexmap" -version = "1.9.3" +name = "num-traits" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", - "hashbrown", ] [[package]] -name = "indicatif" -version = "0.17.3" +name = "once_cell" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cef509aa9bc73864d6756f0d34d35504af3cf0844373afe9b8669a5b8005a729" -dependencies = [ - "console", - "number_prefix", - "portable-atomic 0.3.20", - "unicode-width", -] +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] -name = "inout" -version = "0.1.3" +name = "option-ext" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" -dependencies = [ - "block-padding", - "generic-array", -] +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] -name = "instant" -version = "0.1.12" +name = "parking_lot" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", + "lock_api", + "parking_lot_core", ] [[package]] -name = "io-lifetimes" -version = "1.0.10" +name = "parking_lot_core" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" dependencies = [ - "hermit-abi 0.3.1", + "cfg-if", "libc", - "windows-sys 0.48.0", + "redox_syscall 0.3.5", + "smallvec", + "windows-targets 0.48.5", ] [[package]] -name = "ipnet" -version = "2.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" - -[[package]] -name = "is-terminal" -version = "0.4.7" +name = "pin-project-lite" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" -dependencies = [ - "hermit-abi 0.3.1", - "io-lifetimes", - "rustix", - "windows-sys 0.48.0", -] +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] -name = "itoa" -version = "1.0.6" +name = "pin-utils" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] -name = "jobserver" -version = "0.1.26" +name = "predicates" +version = "2.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd" dependencies = [ - "libc", + "difflib", + "float-cmp", + "itertools", + "normalize-line-endings", + "predicates-core", + "regex", ] [[package]] -name = "js-sys" -version = "0.3.61" +name = "predicates" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +checksum = "09963355b9f467184c04017ced4a2ba2d75cbcb4e7462690d388233253d4b1a9" dependencies = [ - "wasm-bindgen", + "anstyle", + "difflib", + "itertools", + "predicates-core", ] [[package]] -name = "lazy_static" -version = "1.4.0" +name = "predicates-core" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" [[package]] -name = "libc" -version = "0.2.142" +name = "predicates-tree" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" - -[[package]] -name = "libgit2-sys" -version = "0.15.1+1.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb4577bde8cdfc7d6a2a4bcb7b049598597de33ffd337276e9c7db6cd4a2cee7" -dependencies = [ - "cc", - "libc", - "libssh2-sys", - "libz-sys", - "openssl-sys", - "pkg-config", -] - -[[package]] -name = "libssh2-sys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dc8a030b787e2119a731f1951d6a773e2280c660f8ec4b0f5e1505a386e71ee" -dependencies = [ - "cc", - "libc", - "libz-sys", - "openssl-sys", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "libz-sys" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ee889ecc9568871456d42f603d6a0ce59ff328d291063a45cbdf0036baf6db" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "linux-raw-sys" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b64f40e5e03e0d54f03845c8197d0291253cdbedfb1cb46b13c2c117554a9f4c" - -[[package]] -name = "log" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "mio" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys 0.45.0", -] - -[[package]] -name = "ngit" -version = "0.0.1" -dependencies = [ - "clap", - "confy", - "dialoguer", - "git2", - "indicatif", - "nostr", - "nostr-sdk", - "serde", - "serde_json", - "thiserror", -] - -[[package]] -name = "nostr" -version = "0.21.0" -source = "git+https://github.com/DanConwayDev/nostr.git#d2caf5521e06849ecf1b5663a4b70e3cc6c34a69" -dependencies = [ - "aes", - "base64 0.21.0", - "bech32", - "bip39", - "bitcoin", - "bitcoin_hashes 0.12.0", - "cbc", - "getrandom", - "instant", - "log", - "reqwest", - "secp256k1", - "serde", - "serde_json", - "thiserror", - "url", -] - -[[package]] -name = "nostr-sdk" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d60df29fc0725e362c26e8b776dfa41ca23dc157fbb41ca81fdd6705268838c" -dependencies = [ - "gloo-timers", - "log", - "nostr", - "nostr-sdk-net", - "once_cell", - "thiserror", - "tokio", - "wasm-bindgen-futures", -] - -[[package]] -name = "nostr-sdk-net" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5669dfdfeae7c85cfafa59bca1fadedbd8e494450a7e7249d6ccdcd4c41dcd2a" -dependencies = [ - "futures-util", - "thiserror", - "tokio", - "tokio-rustls", - "tokio-socks", - "tokio-tungstenite", - "url", - "webpki", - "webpki-roots", - "ws_stream_wasm", -] - -[[package]] -name = "num_cpus" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" -dependencies = [ - "hermit-abi 0.2.6", - "libc", -] - -[[package]] -name = "number_prefix" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" - -[[package]] -name = "once_cell" -version = "1.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e17f59264b2809d77ae94f0e1ebabc434773f370d6ca667bd223ea10e06cc7e" +checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", + "predicates-core", + "termtree", ] [[package]] -name = "percent-encoding" -version = "2.2.0" +name = "proc-macro-error" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" - -[[package]] -name = "pharos" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9567389417feee6ce15dd6527a8a1ecac205ef62c2932bcf3d9f6fc5b78b414" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ - "futures", - "rustc_version", + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", ] [[package]] -name = "pin-project-lite" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkg-config" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" - -[[package]] -name = "portable-atomic" -version = "0.3.20" +name = "proc-macro-error-attr" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e30165d31df606f5726b090ec7592c308a0eaf61721ff64c9a3018e344a8753e" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "portable-atomic 1.3.1", + "proc-macro2", + "quote", + "version_check", ] -[[package]] -name = "portable-atomic" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bbda379e6e462c97ea6afe9f6233619b202bbc4968d7caa6917788d2070a044" - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.26" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - [[package]] name = "redox_syscall" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -1110,7 +699,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -1125,178 +714,96 @@ dependencies = [ ] [[package]] -name = "reqwest" -version = "0.11.17" +name = "regex" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13293b639a097af28fc8a90f22add145a9c954e49d77da06263d58cf44d5fb91" +checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" dependencies = [ - "base64 0.21.0", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-rustls", - "ipnet", - "js-sys", - "log", - "mime", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls", - "rustls-pemfile", - "serde", - "serde_json", - "serde_urlencoded", - "tokio", - "tokio-rustls", - "tokio-socks", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "webpki-roots", - "winreg", + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", ] [[package]] -name = "ring" -version = "0.16.20" +name = "regex-automata" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" dependencies = [ - "cc", - "libc", - "once_cell", - "spin", - "untrusted", - "web-sys", - "winapi", + "aho-corasick", + "memchr", + "regex-syntax", ] [[package]] -name = "rustc_version" -version = "0.4.0" +name = "regex-syntax" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" + +[[package]] +name = "rexpect" +version = "0.5.0" +source = "git+https://github.com/phaer/rexpect.git?branch=skip-ansi-escape-codes#f099dc4750e38a1c31d2ab06d7d3e0352679d85a" dependencies = [ - "semver", + "comma", + "nix", + "regex", + "tempfile", + "thiserror", ] [[package]] name = "rustix" -version = "0.37.18" +version = "0.38.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bbfc1d1c7c40c01715f47d71444744a81669ca84e8b63e25a55e169b1f86433" +checksum = "d7db8590df6dfcd144d22afd1b83b36c21a18d7cbc1dc4bb5295a8712e9eb662" dependencies = [ - "bitflags", + "bitflags 2.4.0", "errno", - "io-lifetimes", "libc", "linux-raw-sys", "windows-sys 0.48.0", ] -[[package]] -name = "rustls" -version = "0.20.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" -dependencies = [ - "log", - "ring", - "sct", - "webpki", -] - -[[package]] -name = "rustls-pemfile" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" -dependencies = [ - "base64 0.21.0", -] - [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] -name = "sct" -version = "0.7.0" +name = "scopeguard" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "secp256k1" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25996b82292a7a57ed3508f052cfff8640d38d32018784acd714758b43da9c8f" -dependencies = [ - "bitcoin_hashes 0.12.0", - "rand", - "secp256k1-sys", - "serde", -] - -[[package]] -name = "secp256k1-sys" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a129b9e9efbfb223753b9163c4ab3b13cff7fd9c7f010fbac25ab4099fa07e" -dependencies = [ - "cc", -] - -[[package]] -name = "semver" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" - -[[package]] -name = "send_wrapper" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.160" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.160" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.32", ] [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "2cc66a619ed80bf7a0f6b17dd063a84b88f6dea1813737cf469aef1d081142c2" dependencies = [ "itoa", "ryu", @@ -1304,26 +811,28 @@ dependencies = [ ] [[package]] -name = "serde_urlencoded" -version = "0.7.1" +name = "serial_test" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +checksum = "0e56dd856803e253c8f298af3f4d7eb0ae5e23a737252cd90bb4f3b435033b2d" dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", + "dashmap", + "futures", + "lazy_static", + "log", + "parking_lot", + "serial_test_derive", ] [[package]] -name = "sha1" -version = "0.10.5" +name = "serial_test_derive" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" dependencies = [ - "cfg-if", - "cpufeatures", - "digest", + "proc-macro2", + "quote", + "syn 2.0.32", ] [[package]] @@ -1334,28 +843,27 @@ checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" [[package]] name = "slab" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] -name = "socket2" -version = "0.4.9" +name = "smallvec" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" -dependencies = [ - "libc", - "winapi", -] +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" [[package]] -name = "spin" -version = "0.5.2" +name = "strip-ansi-escapes" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +checksum = "55ff8ef943b384c414f54aefa961dd2bd853add74ec75e7ac74cf91dba62bcfa" +dependencies = [ + "vte", +] [[package]] name = "strsim" @@ -1376,9 +884,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.15" +version = "2.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2" dependencies = [ "proc-macro2", "quote", @@ -1387,221 +895,60 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.5.0" +version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" dependencies = [ "cfg-if", "fastrand", "redox_syscall 0.3.5", "rustix", - "windows-sys 0.45.0", -] - -[[package]] -name = "thiserror" -version = "1.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" -dependencies = [ - "thiserror-impl", + "windows-sys 0.48.0", ] [[package]] -name = "thiserror-impl" -version = "1.0.40" +name = "termtree" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.15", -] +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +name = "test_utils" +version = "0.1.0" dependencies = [ - "tinyvec_macros", + "anyhow", + "assert_cmd", + "dialoguer", + "directories", + "rexpect", + "strip-ansi-escapes", ] [[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.28.0" +name = "thiserror" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c786bf8134e5a3a166db9b29ab8f48134739014a3eca7bc6bfa95d673b136f" +checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" dependencies = [ - "autocfg", - "bytes", - "libc", - "mio", - "num_cpus", - "pin-project-lite", - "socket2", - "tokio-macros", - "windows-sys 0.48.0", + "thiserror-impl", ] [[package]] -name = "tokio-macros" -version = "2.1.0" +name = "thiserror-impl" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", -] - -[[package]] -name = "tokio-rustls" -version = "0.23.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" -dependencies = [ - "rustls", - "tokio", - "webpki", -] - -[[package]] -name = "tokio-socks" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51165dfa029d2a65969413a6cc96f354b86b464498702f174a4efa13608fd8c0" -dependencies = [ - "either", - "futures-util", - "thiserror", - "tokio", -] - -[[package]] -name = "tokio-tungstenite" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54319c93411147bced34cb5609a80e0a8e44c5999c93903a81cd866630ec0bfd" -dependencies = [ - "futures-util", - "log", - "rustls", - "tokio", - "tokio-rustls", - "tungstenite", - "webpki", - "webpki-roots", + "syn 2.0.32", ] -[[package]] -name = "tokio-util" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", - "tracing", -] - -[[package]] -name = "toml" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" -dependencies = [ - "serde", -] - -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - -[[package]] -name = "tracing" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" -dependencies = [ - "cfg-if", - "pin-project-lite", - "tracing-core", -] - -[[package]] -name = "tracing-core" -version = "0.1.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" -dependencies = [ - "once_cell", -] - -[[package]] -name = "try-lock" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" - -[[package]] -name = "tungstenite" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ee6ab729cd4cf0fd55218530c4522ed30b7b6081752839b68fcec8d0960788" -dependencies = [ - "base64 0.13.1", - "byteorder", - "bytes", - "http", - "httparse", - "log", - "rand", - "rustls", - "sha1", - "thiserror", - "url", - "utf-8", - "webpki", -] - -[[package]] -name = "typenum" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" - -[[package]] -name = "unicode-bidi" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" - [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" - -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "unicode-width" @@ -1609,42 +956,12 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - -[[package]] -name = "url" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] - -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - [[package]] name = "utf8parse" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "version_check" version = "0.9.4" @@ -1652,152 +969,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] -name = "want" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" -dependencies = [ - "log", - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 1.0.109", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.84" +name = "vte" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +checksum = "f5022b5fbf9407086c180e9557be968742d839e68346af7792b8592489732197" dependencies = [ - "quote", - "wasm-bindgen-macro-support", + "utf8parse", + "vte_generate_state_changes", ] [[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.84" +name = "vte_generate_state_changes" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", - "wasm-bindgen-backend", - "wasm-bindgen-shared", ] [[package]] -name = "wasm-bindgen-shared" -version = "0.2.84" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" - -[[package]] -name = "web-sys" -version = "0.3.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "webpki-roots" -version = "0.22.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" -dependencies = [ - "webpki", -] - -[[package]] -name = "winapi" -version = "0.3.9" +name = "wait-timeout" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", + "libc", ] [[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-sys" -version = "0.42.0" +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "windows-sys" @@ -1814,7 +1018,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.0", + "windows-targets 0.48.5", ] [[package]] @@ -1834,17 +1038,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -1855,9 +1059,9 @@ checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_msvc" @@ -1867,9 +1071,9 @@ checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_aarch64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_i686_gnu" @@ -1879,9 +1083,9 @@ checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_msvc" @@ -1891,9 +1095,9 @@ checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_x86_64_gnu" @@ -1903,9 +1107,9 @@ checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnullvm" @@ -1915,9 +1119,9 @@ checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_msvc" @@ -1927,37 +1131,9 @@ checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" - -[[package]] -name = "winreg" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" -dependencies = [ - "winapi", -] - -[[package]] -name = "ws_stream_wasm" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7999f5f4217fe3818726b66257a4475f71e74ffd190776ad053fa159e50737f5" -dependencies = [ - "async_io_stream", - "futures", - "js-sys", - "log", - "pharos", - "rustc_version", - "send_wrapper", - "thiserror", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "zeroize" diff --git a/Cargo.toml b/Cargo.toml index ac3e916..e745441 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "ngit" version = "0.0.1" edition = "2021" -description = "a proof of concept cli for a nostr based github alternative" +description = "cli for code collaboration over nostr" authors = ["DanConwayDev "] readme = "README.md" homepage = "https://github.com/DanConwayDev/ngit-cli" @@ -12,17 +12,21 @@ keywords = ["nostr", "git"] categories = ["command-line-utilities","git"] [dependencies] -clap = { version = "4.1.6", features = ["derive"] } -nostr = { version = "0.21" } -nostr-sdk = { version = "0.21", features = ["blocking"] } -serde = { version = "1.0.147", features = ["derive"] } -serde_json = "1.0.91" +anyhow = "1.0.75" +clap = { version = "4.3.19", features = ["derive"] } dialoguer = "0.10.4" -indicatif = "0.17.3" -thiserror = "1.0" -confy = "0.5.1" -git2 = "0.17.1" +directories = "5.0.1" +serde = { version = "1.0.181", features = ["derive"] } +serde_json = "1.0.105" -[patch.crates-io] -nostr = { git = 'https://github.com/DanConwayDev/nostr.git', features = ["blocking"] } +[dev-dependencies] +assert_cmd = "2.0.12" +duplicate = "1.0.0" +mockall = "0.11.4" +serial_test = "2.0.0" +test_utils = { path = "test_utils" } +[workspace] +members = [ + "test_utils", +] diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..dfdef63 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 DanConwayDev + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..b9aacd7 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# ngit + +cli for code collaboration over nostr diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..29425ed --- /dev/null +++ b/flake.lock @@ -0,0 +1,130 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1692799911, + "narHash": "sha256-3eihraek4qL744EvQXsK1Ha6C3CR7nnT8X2qWap4RNk=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "f9e7cf818399d17d347f847525c5a5a8032e4e44", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1681202837, + "narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "cfacdce06f30d2b68473a46042957675eebb3401", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1694183432, + "narHash": "sha256-YyPGNapgZNNj51ylQMw9lAgvxtM2ai1HZVUu3GS8Fng=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "db9208ab987cdeeedf78ad9b4cf3c55f5ebd269b", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1681358109, + "narHash": "sha256-eKyxW4OohHQx9Urxi7TQlFBTDWII+F+x2hklDOQPB50=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "96ba1c52e54e74c3197f4d43026b3f3d92e83ff9", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay" + } + }, + "rust-overlay": { + "inputs": { + "flake-utils": "flake-utils_2", + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1694398298, + "narHash": "sha256-Hi904+2V5DJhFdEy9DcARSRrGJOlYSILHUC6CgTtuZU=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "6c520f2e31f4bebeb29cc4563543de7187013575", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..7c36e2d --- /dev/null +++ b/flake.nix @@ -0,0 +1,52 @@ +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + rust-overlay.url = "github:oxalica/rust-overlay"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, rust-overlay, flake-utils, ... }: + flake-utils.lib.eachDefaultSystem (system: + let + overlays = [ (import rust-overlay) ]; + pkgs = import nixpkgs { + inherit system overlays; + }; + in + with pkgs; + { + devShells.default = mkShell { + + nativeBuildInputs = [ + # stable to be introduced when the following issue is resolved + # https://github.com/oxalica/rust-overlay/issues/136 + # rust-bin.stable.latest.default + # nightly for rustfmt + ( + rust-bin.selectLatestNightlyWith (toolchain: toolchain.default.override { + extensions = [ + "rust-src" + "rustfmt" + "clippy" + ]; + }) + ) + ]; + + buildInputs = [ + rust-analyzer + gitlint + ]; + shellHook = '' + # auto-install git hooks + dot_git="$(git rev-parse --git-common-dir)" + if [[ ! -d "$dot_git/hooks" ]]; then mkdir "$dot_git/hooks"; fi + for hook in git_hooks/* ; do ln -sf "$(pwd)/$hook" "$dot_git/hooks/" ; done + + # For rust-analyzer 'hover' tooltips to work. + export RUST_SRC_PATH=${pkgs.rustPlatform.rustLibSrc} + ''; + }; + } + ); +} diff --git a/git_hooks/commit-msg b/git_hooks/commit-msg new file mode 100755 index 0000000..e754e8d --- /dev/null +++ b/git_hooks/commit-msg @@ -0,0 +1,35 @@ +#!/bin/sh +### gitlint commit-msg hook start ### + +# Determine whether we have a tty available by trying to access it. +# This allows us to deal with UI based gitclient's like Atlassian SourceTree. +# NOTE: "exec < /dev/tty" sets stdin to the keyboard +stdin_available=1 +(exec < /dev/tty) 2> /dev/null || stdin_available=0 + +if [ $stdin_available -eq 1 ]; then + # Now that we know we have a functional tty, set stdin to it so we can ask the user questions :-) + exec < /dev/tty + + # On Windows, we need to explicitly set our stdout to the tty to make terminal editing work (e.g. vim) + # See SO for windows detection in bash (slight modified to work on plain shell (not bash)): + # https://stackoverflow.com/questions/394230/how-to-detect-the-os-from-a-bash-script + if [ "$OSTYPE" = "cygwin" ] || [ "$OSTYPE" = "msys" ] || [ "$OSTYPE" = "win32" ]; then + exec > /dev/tty + fi +fi + +gitlint --staged --msg-filename "$1" run-hook +exit_code=$? + +# If we fail to find the gitlint binary (command not found), let's retry by executing as a python module. +# This is the case for Atlassian SourceTree, where $PATH deviates from the user's shell $PATH. +if [ $exit_code -eq 127 ]; then + echo "Fallback to python module execution" + python -m gitlint.cli --staged --msg-filename "$1" run-hook + exit_code=$? +fi + +exit $exit_code + +### gitlint commit-msg hook end ### diff --git a/git_hooks/pre-commit b/git_hooks/pre-commit new file mode 100755 index 0000000..63b191d --- /dev/null +++ b/git_hooks/pre-commit @@ -0,0 +1,24 @@ +#!/bin/sh + +set -eu + +if ! cargo fmt -- --check +then + echo "There are some code style issues." + echo "Run cargo fmt first." + exit 1 +fi + +if ! cargo clippy --all-targets -- -D warnings +then + echo "There are some clippy issues." + exit 1 +fi + +if ! cargo test +then + echo "There are some test issues." + exit 1 +fi + +exit 0 \ No newline at end of file diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..bcf7279 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,13 @@ +unstable_features = true +version = "Two" + +# Imports +imports_granularity = "Crate" +group_imports = "StdExternalCrate" + +# Consistency +newline_style = "Unix" + +# Comments +wrap_comments = true +format_code_in_doc_comments = true diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..8745f50 --- /dev/null +++ b/shell.nix @@ -0,0 +1 @@ +(builtins.getFlake ("git+file://" + toString ./.)).devShells.${builtins.currentSystem}.default diff --git a/src/cli_interactor.rs b/src/cli_interactor.rs new file mode 100644 index 0000000..2f28aee --- /dev/null +++ b/src/cli_interactor.rs @@ -0,0 +1,34 @@ +use anyhow::{bail, Result}; +use dialoguer::{theme::ColorfulTheme, Input}; +#[cfg(test)] +use mockall::*; + +#[derive(Default)] +pub struct Interactor { + theme: ColorfulTheme, +} + +#[cfg_attr(test, automock)] +pub trait InteractorPrompt { + fn input(&self, parms: PromptInputParms) -> Result; +} +impl InteractorPrompt for Interactor { + fn input(&self, parms: PromptInputParms) -> Result { + let input: String = Input::with_theme(&self.theme) + .with_prompt(parms.prompt) + .interact_text()?; + Ok(input) + } +} + +#[derive(Default)] +pub struct PromptInputParms { + pub prompt: String, +} + +impl PromptInputParms { + pub fn with_prompt>(mut self, prompt: S) -> Self { + self.prompt = prompt.into(); + self + } +} diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..b26dea0 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,152 @@ +use std::{fs::File, io::BufReader}; + +use anyhow::{anyhow, Context, Result}; +use directories::ProjectDirs; +#[cfg(test)] +use mockall::*; +use serde::{self, Deserialize, Serialize}; + +#[derive(Default)] +#[allow(clippy::module_name_repetitions)] +pub struct ConfigManager; + +#[cfg_attr(test, automock)] +#[allow(clippy::module_name_repetitions)] +pub trait ConfigManagement { + fn load(&self) -> Result; + fn save(&self, cfg: &MyConfig) -> Result<()>; +} + +pub fn get_dirs() -> Result { + ProjectDirs::from("", "CodeCollaboration", "ngit").ok_or(anyhow!( + "should find operating system home directories with rust-directories crate" + )) +} + +impl ConfigManagement for ConfigManager { + fn load(&self) -> Result { + let config_path = get_dirs()?.config_dir().join("config.json"); + if config_path.exists() { + let file = + File::open(config_path).context("should open application configuration file")?; + let reader = BufReader::new(file); + let config: MyConfig = serde_json::from_reader(reader) + .context("should read config from config file with serde_json")?; + Ok(config) + } else { + Ok(MyConfig::default()) + } + } + fn save(&self, cfg: &MyConfig) -> Result<()> { + let dirs = get_dirs()?; + let config_path = dirs.config_dir().join("config.json"); + let file = if config_path.exists() { + std::fs::OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open(config_path) + .context( + "should open application configuration file with write and truncate options", + )? + } else { + std::fs::create_dir_all(dirs.config_dir()) + .context("should create application config directories")?; + std::fs::File::create(config_path).context("should create application config file")? + }; + serde_json::to_writer_pretty(file, cfg) + .context("should write configuration to config file with serde_json") + } +} + +#[derive(Serialize, Deserialize, Clone, Default, Debug, PartialEq, Eq)] +#[allow(clippy::module_name_repetitions)] +pub struct MyConfig { + pub version: u8, + pub users: Vec, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] +pub struct UserRef { + pub nsec: String, +} + +#[cfg(test)] +mod tests { + use anyhow::Result; + use serial_test::serial; + use test_utils::*; + + use super::*; + + mod load { + use super::*; + + #[test] + #[serial] + fn when_config_file_doesnt_exist_defaults_are_returned() -> Result<()> { + with_fresh_config(|| { + assert_eq!(ConfigManager.load()?, MyConfig::default()); + + Ok(()) + }) + } + + #[test] + #[serial] + fn when_config_file_exists_it_is_returned() -> Result<()> { + with_fresh_config(|| { + let c = ConfigManager; + let new_config = MyConfig { + version: 255, + ..MyConfig::default() + }; + c.save(&new_config)?; + assert_eq!(c.load()?, new_config); + + Ok(()) + }) + } + } + + mod save { + use super::*; + + #[test] + #[serial] + fn when_config_file_doesnt_config_is_saved() -> Result<()> { + with_fresh_config(|| { + let c = ConfigManager; + let new_config = MyConfig { + version: 255, + ..MyConfig::default() + }; + c.save(&new_config)?; + assert_eq!(c.load()?, new_config); + + Ok(()) + }) + } + + #[test] + #[serial] + fn when_config_file_exists_new_config_is_saved() -> Result<()> { + with_fresh_config(|| { + let c = ConfigManager; + let config = MyConfig { + version: 255, + ..MyConfig::default() + }; + c.save(&config)?; + let new_config = MyConfig { + version: 254, + ..MyConfig::default() + }; + c.save(&new_config)?; + assert_eq!(c.load()?, new_config); + + Ok(()) + }) + } + } +} diff --git a/src/key_handling/mod.rs b/src/key_handling/mod.rs new file mode 100644 index 0000000..913bd46 --- /dev/null +++ b/src/key_handling/mod.rs @@ -0,0 +1 @@ +pub mod users; diff --git a/src/key_handling/users.rs b/src/key_handling/users.rs new file mode 100644 index 0000000..bd1748a --- /dev/null +++ b/src/key_handling/users.rs @@ -0,0 +1,124 @@ +use anyhow::{Context, Result}; + +use crate::{ + cli_interactor::{Interactor, InteractorPrompt, PromptInputParms}, + config::{ConfigManagement, ConfigManager, MyConfig, UserRef}, +}; + +#[derive(Default)] +pub struct UserManager { + config_manager: ConfigManager, + interactor: Interactor, +} + +pub trait UserManagement { + fn add(&self, nsec: &Option) -> Result<()>; +} + +#[cfg(test)] +use duplicate::duplicate_item; +#[cfg_attr(test, duplicate_item(UserManager; [UserManager]; [self::tests::MockUserManager]))] +impl UserManagement for UserManager { + fn add(&self, nsec: &Option) -> Result<()> { + let nsec = match nsec.clone() { + Some(nsec) => nsec, + None => self + .interactor + .input( + PromptInputParms::default().with_prompt("login with nsec (or hex private key)"), + ) + .context("failed to get nsec input from interactor.input")?, + }; + + self.config_manager + .save(&MyConfig { + users: vec![UserRef { + nsec: nsec.to_string(), + }], + ..MyConfig::default() + }) + .context("failed to save application configuration with new user details in")?; + + println!("logged in as {nsec}"); + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use test_utils::*; + + use super::*; + use crate::{cli_interactor::MockInteractorPrompt, config::MockConfigManagement}; + + #[derive(Default)] + pub struct MockUserManager { + pub config_manager: MockConfigManagement, + pub interactor: MockInteractorPrompt, + } + + mod add { + use super::*; + + impl MockUserManager { + fn add_return_expected_responses(mut self) -> Self { + self.config_manager + .expect_load() + .returning(|| Ok(MyConfig::default())); + self.config_manager.expect_save().returning(|_| Ok(())); + self.interactor + .expect_input() + .returning(|_| Ok(TEST_KEY_1_NSEC.into())); + self + } + } + + mod when_nsec_is_passed { + use super::*; + + #[test] + fn user_isnt_prompted() { + let mut m = MockUserManager::default().add_return_expected_responses(); + m.interactor = MockInteractorPrompt::default(); + m.interactor.expect_input().never(); + + let _ = m.add(&Some(TEST_KEY_1_NSEC.into())); + } + } + + mod when_no_nsec_is_passed { + use super::*; + + #[test] + fn prompt_for_nsec() { + let mut m = MockUserManager::default().add_return_expected_responses(); + + m.interactor = MockInteractorPrompt::new(); + m.interactor + .expect_input() + .once() + .withf(|p| p.prompt.eq("login with nsec (or hex private key)")) + .returning(|_| Ok(TEST_KEY_1_NSEC.into())); + + let _ = m.add(&None); + } + + #[test] + fn stored_in_config() { + let mut m = MockUserManager::default().add_return_expected_responses(); + + m.config_manager = MockConfigManagement::new(); + m.config_manager + .expect_load() + .returning(|| Ok(MyConfig::default())); + m.config_manager + .expect_save() + .withf(|cfg| cfg.users.len().eq(&1) && cfg.users[0].nsec.eq(TEST_KEY_1_NSEC)) + .returning(|_| Ok(())); + + let _ = m.add(&None); + } + } + } +} diff --git a/src/login.rs b/src/login.rs new file mode 100644 index 0000000..da19a75 --- /dev/null +++ b/src/login.rs @@ -0,0 +1,16 @@ +use anyhow::{Context, Result}; + +use crate::{ + config::{ConfigManagement, ConfigManager}, + key_handling::users::{UserManagement, UserManager}, +}; + +pub fn launch(nsec: &Option) -> Result<()> { + let cfg = ConfigManager + .load() + .context("failed to load application config")?; + if !cfg.users.is_empty() { + println!("logged in as {}", cfg.users[0].nsec); + } + UserManager::default().add(nsec) +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..d16f1a3 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,35 @@ +#![cfg_attr(not(test), warn(clippy::pedantic))] +#![cfg_attr(not(test), warn(clippy::expect_used))] + +use anyhow::Result; +use clap::{Parser, Subcommand}; + +mod cli_interactor; +mod config; +mod key_handling; +mod login; +mod sub_commands; + +#[derive(Parser)] +#[command(author, version, about, long_about = None)] +#[command(propagate_version = true)] +pub struct Cli { + #[command(subcommand)] + command: Commands, + /// nsec or hex private key + #[arg(short, long)] + nsec: Option, +} + +#[derive(Subcommand)] +enum Commands { + /// save encrypted nsec for future use + Login(sub_commands::login::SubCommandArgs), +} + +fn main() -> Result<()> { + let cli = Cli::parse(); + match &cli.command { + Commands::Login(args) => sub_commands::login::launch(&cli, args), + } +} diff --git a/src/sub_commands/login.rs b/src/sub_commands/login.rs new file mode 100644 index 0000000..d61f578 --- /dev/null +++ b/src/sub_commands/login.rs @@ -0,0 +1,11 @@ +use anyhow::Result; +use clap; + +use crate::{login, Cli}; + +#[derive(clap::Args)] +pub struct SubCommandArgs; + +pub fn launch(args: &Cli, _command_args: &SubCommandArgs) -> Result<()> { + login::launch(&args.nsec) +} diff --git a/src/sub_commands/mod.rs b/src/sub_commands/mod.rs new file mode 100644 index 0000000..320cbbb --- /dev/null +++ b/src/sub_commands/mod.rs @@ -0,0 +1 @@ +pub mod login; diff --git a/test_utils/Cargo.toml b/test_utils/Cargo.toml new file mode 100644 index 0000000..e1f6090 --- /dev/null +++ b/test_utils/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "test_utils" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1.0.75" +assert_cmd = "2.0.12" +dialoguer = "0.10.4" +directories = "5.0.1" +rexpect = { git = "https://github.com/phaer/rexpect.git", branch= "skip-ansi-escape-codes" } +strip-ansi-escapes = "0.2.0" diff --git a/test_utils/src/lib.rs b/test_utils/src/lib.rs new file mode 100644 index 0000000..495e8d2 --- /dev/null +++ b/test_utils/src/lib.rs @@ -0,0 +1,280 @@ +use std::ffi::OsStr; + +use anyhow::{ensure, Context, Result}; +use dialoguer::theme::{ColorfulTheme, Theme}; +use directories::ProjectDirs; +use rexpect::session::{Options, PtySession}; +use strip_ansi_escapes::strip_str; + +pub static TEST_KEY_1_NSEC: &str = + "nsec1ppsg5sm2aexq06juxmu9evtutr6jkwkhp98exxxvwamhru9lyx9s3rwseq"; + +pub static TEST_KEY_2_NSEC: &str = + "nsec1ypglg6nj6ep0g2qmyfqcv2al502gje3jvpwye6mthmkvj93tqkesknv6qm"; + +/// wrapper for a cli testing tool - currently wraps rexpect and dialoguer +/// +/// 1. allow more accurate articulation of expected behaviour +/// 2. provide flexibility to swap rexpect for a tool that better maps to +/// expected behaviour +/// 3. provides flexability to swap dialoguer with another cli interaction tool +pub struct CliTester { + rexpect_session: PtySession, + formatter: ColorfulTheme, +} + +impl CliTester { + pub fn expect_input(&mut self, prompt: &str) -> Result { + let mut i = CliTesterInputPrompt { + tester: self, + prompt: prompt.to_string(), + }; + i.prompt(false).context("initial input prompt")?; + Ok(i) + } + + pub fn expect_input_eventually(&mut self, prompt: &str) -> Result { + let mut i = CliTesterInputPrompt { + tester: self, + prompt: prompt.to_string(), + }; + i.prompt(true).context("initial input prompt")?; + Ok(i) + } +} + +pub struct CliTesterInputPrompt<'a> { + tester: &'a mut CliTester, + prompt: String, +} + +impl CliTesterInputPrompt<'_> { + fn prompt(&mut self, eventually: bool) -> Result<&mut Self> { + let mut s = String::new(); + self.tester + .formatter + .format_prompt(&mut s, self.prompt.as_str()) + .expect("diagluer theme formatter should succeed"); + s.push(' '); + + ensure!( + s.contains(self.prompt.as_str()), + "dialoguer must be broken as formatted prompt success doesnt contain prompt" + ); + + if eventually { + self.tester + .expect_eventually(sanatize(s).as_str()) + .context("expect input prompt eventually")?; + } else { + self.tester + .expect(sanatize(s).as_str()) + .context("expect input prompt")?; + } + + Ok(self) + } + + pub fn succeeds_with(&mut self, input: &str) -> Result<&mut Self> { + self.tester.send_line(input)?; + self.tester + .expect(input) + .context("expect input to be printed")?; + self.tester + .expect("\r") + .context("expect new line after input to be printed")?; + + let mut s = String::new(); + self.tester + .formatter + .format_input_prompt_selection(&mut s, self.prompt.as_str(), input) + .expect("diagluer theme formatter should succeed"); + if !s.contains(self.prompt.as_str()) { + panic!("dialoguer must be broken as formatted prompt success doesnt contain prompt"); + } + let formatted_success = format!("{}\r\n", sanatize(s)); + + self.tester + .expect(formatted_success.as_str()) + .context("expect immediate prompt success")?; + Ok(self) + } +} + +impl CliTester { + pub fn new(args: I) -> Self + where + I: IntoIterator, + S: AsRef, + { + Self { + rexpect_session: rexpect_with(args).expect("rexpect to spawn new process"), + formatter: ColorfulTheme::default(), + } + } + + pub fn restart_with(&mut self, args: I) -> &mut Self + where + I: IntoIterator, + S: AsRef, + { + self.rexpect_session + .process + .exit() + .expect("process to exit"); + self.rexpect_session = rexpect_with(args).expect("rexpect to spawn new process"); + self + } + + pub fn exit(&mut self) -> Result<()> { + match self + .rexpect_session + .process + .exit() + .context("expect proccess to exit") + { + Ok(_) => Ok(()), + Err(e) => Err(e), + } + } + + /// returns what came before expected message + pub fn expect_eventually(&mut self, message: &str) -> Result { + let before = self + .rexpect_session + .exp_string(message) + .context("exp_string failed")?; + Ok(before) + } + + pub fn expect(&mut self, message: &str) -> Result<&mut Self> { + let before = self.expect_eventually(message)?; + ensure!( + before.is_empty(), + format!( + "expected message \"{}\". but got \"{}\" first.", + message.replace('\n', "\\n").replace('\r', "\\r"), + before.replace('\n', "\\n").replace('\r', "\\r"), + ), + ); + Ok(self) + } + + pub fn expect_end(&mut self) -> Result<()> { + let before = self + .rexpect_session + .exp_eof() + .context("expected immediate end but got timed out")?; + ensure!( + before.is_empty(), + format!( + "expected immediate end but got '{}' first.", + before.replace('\n', "\\n").replace('\r', "\\r"), + ), + ); + Ok(()) + } + + pub fn expect_end_with(&mut self, message: &str) -> Result<()> { + let before = self + .rexpect_session + .exp_eof() + .context("expected immediate end but got timed out")?; + assert_eq!(before, message); + Ok(()) + } + pub fn expect_end_eventually(&mut self) -> Result { + self.rexpect_session + .exp_eof() + .context("expected end eventually but got timed out") + } + + pub fn expect_end_eventually_with(&mut self, message: &str) -> Result<()> { + self.expect_eventually(message)?; + self.expect_end() + } + + fn send_line(&mut self, line: &str) -> Result<()> { + self.rexpect_session + .send_line(line) + .context("send_line failed")?; + Ok(()) + } +} + +/// sanatize unicode string for rexpect +fn sanatize(s: String) -> String { + // remove ansi codes as they don't work with rexpect + strip_str(s) + // sanatize unicode rexpect issue 105 is resolved https://github.com/rust-cli/rexpect/issues/105 + .as_bytes() + .iter() + .map(|c| *c as char) + .collect::() +} + +pub fn rexpect_with(args: I) -> Result +where + I: IntoIterator, + S: AsRef, +{ + let mut cmd = std::process::Command::new(assert_cmd::cargo::cargo_bin("ngit")); + cmd.args(args); + // using branch for PR https://github.com/rust-cli/rexpect/pull/103 to strip ansi escape codes + rexpect::session::spawn_with_options( + cmd, + Options { + timeout_ms: Some(2000), + strip_ansi_escape_codes: true, + }, + ) +} + +/// backup and remove application config and data +pub fn before() -> Result<()> { + backup_existing_config() +} + +/// restore backuped application config and data +pub fn after() -> Result<()> { + restore_config_backup() +} + +/// run func between before and after scripts which backup, reset and restore +/// application config +/// +/// TODO: fix issue: if func panics, after() is not run. +pub fn with_fresh_config(func: F) -> Result<()> +where + F: Fn() -> Result<()>, +{ + before()?; + func()?; + after() +} + +fn backup_existing_config() -> Result<()> { + let config_path = get_dirs().config_dir().join("config.json"); + let backup_config_path = get_dirs().config_dir().join("config-backup.json"); + if config_path.exists() { + std::fs::rename(config_path, backup_config_path)?; + } + Ok(()) +} + +fn restore_config_backup() -> Result<()> { + let config_path = get_dirs().config_dir().join("config.json"); + let backup_config_path = get_dirs().config_dir().join("config-backup.json"); + if config_path.exists() { + std::fs::remove_file(&config_path)?; + } + if backup_config_path.exists() { + std::fs::rename(backup_config_path, config_path)?; + } + Ok(()) +} + +fn get_dirs() -> ProjectDirs { + ProjectDirs::from("", "CodeCollaboration", "ngit") + .expect("rust directories crate should return ProjectDirs") +} diff --git a/tests/login.rs b/tests/login.rs new file mode 100644 index 0000000..a7e1889 --- /dev/null +++ b/tests/login.rs @@ -0,0 +1,145 @@ +use anyhow::Result; +use serial_test::serial; +use test_utils::*; + +static EXPECTED_NSEC_PROMPT: &str = "login with nsec (or hex private key)"; + +fn standard_login() -> Result { + let mut p = CliTester::new(["login"]); + + p.expect_input_eventually(EXPECTED_NSEC_PROMPT)? + .succeeds_with(TEST_KEY_1_NSEC)?; + + p.expect_end_eventually()?; + Ok(p) +} + +mod when_first_time_login { + use super::*; + + #[test] + #[serial] + fn prompts_for_nsec() -> Result<()> { + with_fresh_config(|| { + standard_login()?; + Ok(()) + }) + } + + #[test] + #[serial] + fn succeeds_with_text_logged_in_as_npub() -> Result<()> { + with_fresh_config(|| { + let mut p = CliTester::new(["login"]); + + p.expect_input(EXPECTED_NSEC_PROMPT)? + .succeeds_with(TEST_KEY_1_NSEC)?; + + p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_1_NSEC).as_str()) + }) + } + + #[test] + #[serial] + fn next_time_returns_logged_in_as_npub() -> Result<()> { + with_fresh_config(|| { + standard_login()?.exit()?; + + CliTester::new(["login"]) + .expect(format!("logged in as {}\r\n", TEST_KEY_1_NSEC).as_str())? + .exit() + }) + } +} + +mod when_called_with_nsec_parameter { + use super::*; + + #[test] + #[serial] + fn valid_nsec_param_succeeds_without_prompts() -> Result<()> { + with_fresh_config(|| { + CliTester::new(["--nsec", TEST_KEY_2_NSEC, "login"]) + .expect_end_with(format!("logged in as {}\r\n", TEST_KEY_2_NSEC).as_str())?; + + CliTester::new(["login"]) + .expect(format!("logged in as {}\r\n", TEST_KEY_2_NSEC).as_str())? + .exit() + }) + } + + mod when_logging_in_as_different_nsec { + use super::*; + + #[test] + #[serial] + fn valid_nsec_param_succeeds_without_prompts_and_logs_in() -> Result<()> { + with_fresh_config(|| { + standard_login()?.exit()?; + + CliTester::new(["--nsec", TEST_KEY_2_NSEC, "login"]) + .expect(format!("logged in as {}\r\n", TEST_KEY_1_NSEC).as_str())? + .expect_end_with(format!("logged in as {}\r\n", TEST_KEY_2_NSEC).as_str())?; + + CliTester::new(["login"]) + .expect(format!("logged in as {}\r\n", TEST_KEY_2_NSEC).as_str())? + .exit() + }) + } + } +} + +mod when_logged_in { + use super::*; + + #[test] + #[serial] + fn returns_logged_in_as_npub() -> Result<()> { + with_fresh_config(|| { + standard_login()?.exit()?; + + CliTester::new(["login"]) + .expect(format!("logged in as {}\r\n", TEST_KEY_1_NSEC).as_str())? + .exit() + }) + } + + #[test] + #[serial] + fn prompts_to_log_in_with_different_nsec() -> Result<()> { + with_fresh_config(|| { + standard_login()?.exit()?; + + let mut p = CliTester::new(["login"]); + p.expect(format!("logged in as {}\r\n", TEST_KEY_1_NSEC).as_str())?; + + p.expect_input(EXPECTED_NSEC_PROMPT)? + .succeeds_with(TEST_KEY_2_NSEC)?; + + p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_2_NSEC).as_str()) + }) + } + mod when_logging_in_as_different_nsec { + use super::*; + + #[test] + #[serial] + fn confirmed_as_logged_in_as_additional_user() -> Result<()> { + with_fresh_config(|| { + standard_login()?.exit()?; + + let mut p = CliTester::new(["login"]); + p.expect(format!("logged in as {}\r\n", TEST_KEY_1_NSEC).as_str())?; + + p.expect_input(EXPECTED_NSEC_PROMPT)? + .succeeds_with(TEST_KEY_2_NSEC)?; + + p.expect_end_with(format!("logged in as {}\r\n", TEST_KEY_2_NSEC).as_str())?; + + CliTester::new(["login"]) + .expect(format!("logged in as {}\r\n", TEST_KEY_2_NSEC).as_str())? + .exit() + }) + } + } +} -- cgit v1.2.3