From 80d9db4e972cb82a36d32223562328a8b969183a Mon Sep 17 00:00:00 2001 From: Alexander Schmidt Date: Fri, 15 Sep 2023 15:28:01 +0200 Subject: [PATCH 01/10] rustyline dep --- Cargo.lock | 248 +++++++++++++++++++++++++------------------------ cli/Cargo.toml | 11 ++- 2 files changed, 135 insertions(+), 124 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a8c3289365..dffc91d006 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -113,7 +113,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" dependencies = [ - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -123,7 +123,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd" dependencies = [ "anstyle", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -512,19 +512,30 @@ dependencies = [ "chrono", "clap", "colored 2.0.4", - "dialoguer", "dotenvy", "fern-logger", "humantime", "iota-sdk", "log", "prefix-hex", + "rustyline", "serde_json", "thiserror", "tokio", "zeroize", ] +[[package]] +name = "clipboard-win" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7191c27c2357d9b7ef96baac1773290d4ca63b24205b82a3fd8a0637afcf0362" +dependencies = [ + "error-code", + "str-buf", + "winapi", +] + [[package]] name = "colorchoice" version = "1.0.0" @@ -550,20 +561,7 @@ checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6" dependencies = [ "is-terminal", "lazy_static", - "windows-sys 0.48.0", -] - -[[package]] -name = "console" -version = "0.15.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" -dependencies = [ - "encode_unicode", - "lazy_static", - "libc", - "unicode-width", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] @@ -754,17 +752,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "dialoguer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59c6f2989294b9a498d3ad5491a79c6deb604617378e1cdc4bfc1c1361fe2f87" -dependencies = [ - "console", - "shell-words", - "zeroize", -] - [[package]] name = "digest" version = "0.9.0" @@ -907,12 +894,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "encode_unicode" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" - [[package]] name = "encoding_rs" version = "0.8.33" @@ -922,6 +903,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "endian-type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" + [[package]] name = "enum-iterator" version = "0.6.0" @@ -956,7 +943,7 @@ checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" dependencies = [ "errno-dragonfly", "libc", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -969,6 +956,27 @@ dependencies = [ "libc", ] +[[package]] +name = "error-code" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64f18991e7bf11e7ffee451b5318b5c1a73c52d0d0ada6e5a3017c8c1ced6a21" +dependencies = [ + "libc", + "str-buf", +] + +[[package]] +name = "fd-lock" +version = "3.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5" +dependencies = [ + "cfg-if", + "rustix", + "windows-sys", +] + [[package]] name = "fern" version = "0.6.2" @@ -1337,6 +1345,15 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "home" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +dependencies = [ + "windows-sys", +] + [[package]] name = "http" version = "0.2.9" @@ -1715,7 +1732,7 @@ checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi", "rustix", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -1972,7 +1989,7 @@ checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", "wasi", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -2025,6 +2042,15 @@ dependencies = [ "smallvec", ] +[[package]] +name = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", +] + [[package]] name = "nix" version = "0.24.3" @@ -2037,6 +2063,17 @@ dependencies = [ "memoffset 0.6.5", ] +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", +] + [[package]] name = "no-std-compat" version = "0.4.1" @@ -2179,7 +2216,7 @@ dependencies = [ "libc", "redox_syscall 0.3.5", "smallvec", - "windows-targets 0.48.5", + "windows-targets", ] [[package]] @@ -2463,6 +2500,16 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +[[package]] +name = "radix_trie" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" +dependencies = [ + "endian-type", + "nibble_vec", +] + [[package]] name = "rand" version = "0.8.5" @@ -2701,7 +2748,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -2757,6 +2804,29 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustyline" +version = "12.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "994eca4bca05c87e86e15d90fc7a91d1be64b4482b38cb2d27474568fe7c9db9" +dependencies = [ + "bitflags 2.4.0", + "cfg-if", + "clipboard-win", + "fd-lock", + "home", + "libc", + "log", + "memchr", + "nix 0.26.4", + "radix_trie", + "scopeguard", + "unicode-segmentation", + "unicode-width", + "utf8parse", + "winapi", +] + [[package]] name = "ryu" version = "1.0.15" @@ -2787,7 +2857,7 @@ version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -2962,12 +3032,6 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "shell-words" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" - [[package]] name = "shlex" version = "1.2.0" @@ -3038,7 +3102,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -3072,6 +3136,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "str-buf" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0" + [[package]] name = "stronghold-derive" version = "1.0.0" @@ -3094,7 +3164,7 @@ dependencies = [ "libc", "libsodium-sys", "log", - "nix", + "nix 0.24.3", "rand", "serde", "thiserror", @@ -3284,7 +3354,7 @@ dependencies = [ "pin-project-lite", "socket2 0.5.3", "tokio-macros", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -3442,6 +3512,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + [[package]] name = "unicode-width" version = "0.1.10" @@ -3680,37 +3756,13 @@ dependencies = [ "windows_x86_64_msvc 0.36.1", ] -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -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", + "windows-targets", ] [[package]] @@ -3719,21 +3771,15 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_gnullvm", "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_gnullvm", "windows_x86_64_msvc 0.48.5", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -3746,12 +3792,6 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -3764,12 +3804,6 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -3782,12 +3816,6 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -3800,24 +3828,12 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -3830,12 +3846,6 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -3858,7 +3868,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ "cfg-if", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] diff --git a/cli/Cargo.toml b/cli/Cargo.toml index b37d28a8eb..413f9154e3 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -33,16 +33,17 @@ clap = { version = "4.4.2", default-features = false, features = [ "env", ] } colored = { version = "2.0.4", default-features = false } -dialoguer = { version = "0.10.4", default-features = false, features = [ - "history", - "password", - "completion", -] } +#dialoguer = { version = "0.10.4", default-features = false, features = [ +# "history", +# "password", +# "completion", +# } dotenvy = { version = "0.15.7", default-features = false } fern-logger = { version = "0.5.0", default-features = false } humantime = { version = "2.1.0", default-features = false } log = { version = "0.4.20", default-features = false } prefix-hex = { version = "0.7.1", default-features = false, features = ["std"] } +rustyline = { version = "12.0.0" } serde_json = { version = "1.0.105", default-features = false } thiserror = { version = "1.0.48", default-features = false } tokio = { version = "1.32.0", default-features = false, features = ["fs"] } From 88a433554475f56d4202b7632c8cdc301ed83ee9 Mon Sep 17 00:00:00 2001 From: Alexander Schmidt Date: Mon, 18 Sep 2023 09:25:20 +0200 Subject: [PATCH 02/10] finish impl --- Cargo.lock | 149 +++++++++++++-- cli/Cargo.toml | 12 +- cli/src/account.rs | 346 ++++++++++++++++++++-------------- cli/src/account_completion.rs | 26 ++- cli/src/main.rs | 1 - 5 files changed, 354 insertions(+), 180 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dffc91d006..b9da6f093d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -113,7 +113,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" dependencies = [ - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -123,7 +123,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -512,6 +512,7 @@ dependencies = [ "chrono", "clap", "colored 2.0.4", + "dialoguer", "dotenvy", "fern-logger", "humantime", @@ -561,7 +562,20 @@ checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6" dependencies = [ "is-terminal", "lazy_static", - "windows-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "console" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "unicode-width", + "windows-sys 0.45.0", ] [[package]] @@ -752,6 +766,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "dialoguer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59c6f2989294b9a498d3ad5491a79c6deb604617378e1cdc4bfc1c1361fe2f87" +dependencies = [ + "console", + "shell-words", + "zeroize", +] + [[package]] name = "digest" version = "0.9.0" @@ -894,6 +919,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + [[package]] name = "encoding_rs" version = "0.8.33" @@ -943,7 +974,7 @@ checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" dependencies = [ "errno-dragonfly", "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -974,7 +1005,7 @@ checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5" dependencies = [ "cfg-if", "rustix", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1351,7 +1382,7 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" dependencies = [ - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1732,7 +1763,7 @@ checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi", "rustix", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1989,7 +2020,7 @@ checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", "wasi", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -2216,7 +2247,7 @@ dependencies = [ "libc", "redox_syscall 0.3.5", "smallvec", - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -2748,7 +2779,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -2820,6 +2851,7 @@ dependencies = [ "memchr", "nix 0.26.4", "radix_trie", + "rustyline-derive", "scopeguard", "unicode-segmentation", "unicode-width", @@ -2827,6 +2859,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "rustyline-derive" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a32af5427251d2e4be14fc151eabe18abb4a7aad5efee7044da9f096c906a43" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.31", +] + [[package]] name = "ryu" version = "1.0.15" @@ -2857,7 +2900,7 @@ version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -3032,6 +3075,12 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + [[package]] name = "shlex" version = "1.2.0" @@ -3102,7 +3151,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -3354,7 +3403,7 @@ dependencies = [ "pin-project-lite", "socket2 0.5.3", "tokio-macros", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -3756,13 +3805,37 @@ dependencies = [ "windows_x86_64_msvc 0.36.1", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +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", ] [[package]] @@ -3771,15 +3844,21 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", + "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", + "windows_x86_64_gnullvm 0.48.5", "windows_x86_64_msvc 0.48.5", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -3792,6 +3871,12 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -3804,6 +3889,12 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -3816,6 +3907,12 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -3828,12 +3925,24 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -3846,6 +3955,12 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -3868,7 +3983,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ "cfg-if", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 413f9154e3..bfea67e03a 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -33,17 +33,17 @@ clap = { version = "4.4.2", default-features = false, features = [ "env", ] } colored = { version = "2.0.4", default-features = false } -#dialoguer = { version = "0.10.4", default-features = false, features = [ -# "history", -# "password", -# "completion", -# } +dialoguer = { version = "0.10.4", default-features = false, features = [ + #"history", + "password", + #"completion", +] } dotenvy = { version = "0.15.7", default-features = false } fern-logger = { version = "0.5.0", default-features = false } humantime = { version = "2.1.0", default-features = false } log = { version = "0.4.20", default-features = false } prefix-hex = { version = "0.7.1", default-features = false, features = ["std"] } -rustyline = { version = "12.0.0" } +rustyline = { version = "12.0.0", features = ["derive"] } serde_json = { version = "1.0.105", default-features = false } thiserror = { version = "1.0.48", default-features = false } tokio = { version = "1.32.0", default-features = false, features = ["fs"] } diff --git a/cli/src/account.rs b/cli/src/account.rs index aec3cc338e..83d74c6ca6 100644 --- a/cli/src/account.rs +++ b/cli/src/account.rs @@ -1,14 +1,18 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use std::borrow::Cow; + use clap::Parser; use colored::Colorize; -use dialoguer::Input; use iota_sdk::wallet::{Account, Wallet}; +use rustyline::{ + error::ReadlineError, highlight::Highlighter, hint::HistoryHinter, history::MemHistory, Completer, Config, Editor, + Helper, Hinter, Validator, +}; use crate::{ - account_completion::AccountCompletion, - account_history::AccountHistory, + account_completion::AccountCompleter, command::account::{ addresses_command, balance_command, burn_native_token_command, burn_nft_command, claim_command, claimable_outputs_command, consolidate_command, create_alias_outputs_command, create_native_token_command, @@ -24,11 +28,50 @@ use crate::{ println_log_error, }; +#[derive(Helper, Completer, Hinter, Validator)] +pub struct PromptHelper { + #[rustyline(Completer)] + completer: AccountCompleter, + #[rustyline(Hinter)] + hinter: HistoryHinter, + colored_prompt: String, +} + +impl Highlighter for PromptHelper { + fn highlight_prompt<'b, 's: 'b, 'p: 'b>(&'s self, prompt: &'p str, default: bool) -> Cow<'b, str> { + if default { + Cow::Borrowed(&self.colored_prompt) + } else { + Cow::Borrowed(prompt) + } + } + + fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> { + Cow::Owned(hint.bold().to_string()) + } +} + // loop on the account prompt pub async fn account_prompt(wallet: &Wallet, mut account: Account) -> Result<(), Error> { - let mut history = AccountHistory::default(); + let config = Config::builder() + .auto_add_history(true) + .history_ignore_space(true) + .completion_type(rustyline::CompletionType::List) + .edit_mode(rustyline::EditMode::Emacs) + .build(); + + // Panic: should never happen + let mut rl = Editor::with_history(config, MemHistory::with_config(config)).unwrap(); + + let helper = PromptHelper { + completer: AccountCompleter, + hinter: HistoryHinter {}, + colored_prompt: String::new(), + }; + rl.set_helper(Some(helper)); + loop { - match account_prompt_internal(wallet, &account, &mut history).await { + match account_prompt_internal(wallet, &account, &mut rl).await { Ok(res) => match res { AccountPromptResponse::Reprompt => (), AccountPromptResponse::Done => { @@ -55,154 +98,173 @@ pub enum AccountPromptResponse { pub async fn account_prompt_internal( wallet: &Wallet, account: &Account, - history: &mut AccountHistory, + rl: &mut Editor, ) -> Result { let alias = { let account = account.details().await; account.alias().clone() }; - let command: String = Input::new() - .with_prompt(format!("Account \"{}\"", alias).green().to_string()) - .history_with(history) - .completion_with(&AccountCompletion) - .interact_text()?; - match command.as_str() { - "h" | "help" => AccountCli::print_help()?, - "c" | "clear" => { - // Clear console - let _ = std::process::Command::new("clear").status(); - } - "accounts" => { - // List all accounts - let accounts = wallet.get_accounts().await?; - println!("INDEX\tALIAS"); - for account in accounts { - let details = &*account.details().await; - println!("{}\t{}", details.index(), details.alias()); - } - } - _ => { - // Prepend `Account: ` so the parsing will be correct - let command = format!("Account: {}", command.trim()); - let account_cli = match AccountCli::try_parse_from(command.split_whitespace()) { - Ok(account_cli) => account_cli, - Err(err) => { - println!("{err}"); - return Ok(AccountPromptResponse::Reprompt); - } - }; - if let Err(err) = match account_cli.command { - AccountCommand::Addresses => addresses_command(account).await, - AccountCommand::Balance { addresses } => balance_command(account, addresses).await, - AccountCommand::BurnNativeToken { token_id, amount } => { - burn_native_token_command(account, token_id, amount).await - } - AccountCommand::BurnNft { nft_id } => burn_nft_command(account, nft_id).await, - AccountCommand::Claim { output_id } => claim_command(account, output_id).await, - AccountCommand::ClaimableOutputs => claimable_outputs_command(account).await, - AccountCommand::Consolidate => consolidate_command(account).await, - AccountCommand::CreateAliasOutput => create_alias_outputs_command(account).await, - AccountCommand::CreateNativeToken { - circulating_supply, - maximum_supply, - foundry_metadata_hex, - foundry_metadata_file, - } => { - create_native_token_command( - account, - circulating_supply, - maximum_supply, - bytes_from_hex_or_file(foundry_metadata_hex, foundry_metadata_file).await?, - ) - .await - } - AccountCommand::DestroyAlias { alias_id } => destroy_alias_command(account, alias_id).await, - AccountCommand::DestroyFoundry { foundry_id } => destroy_foundry_command(account, foundry_id).await, - AccountCommand::Exit => { - return Ok(AccountPromptResponse::Done); - } - AccountCommand::Faucet { address, url } => faucet_command(account, address, url).await, - AccountCommand::MeltNativeToken { token_id, amount } => { - melt_native_token_command(account, token_id, amount).await - } - AccountCommand::MintNativeToken { token_id, amount } => { - mint_native_token(account, token_id, amount).await + + let prompt = format!("Account \"{}\": ", alias); + // Panic: should never happen since there's always a helper + rl.helper_mut().unwrap().colored_prompt = prompt.green().to_string(); + + let command = rl.readline(&prompt); + match command { + Ok(command) => { + match command.as_str() { + "h" | "help" => AccountCli::print_help()?, + "c" | "clear" => { + // Clear console + let _ = std::process::Command::new("clear").status(); } - AccountCommand::MintNft { - address, - immutable_metadata_hex, - immutable_metadata_file, - metadata_hex, - metadata_file, - tag, - sender, - issuer, - } => { - mint_nft_command( - account, - address, - bytes_from_hex_or_file(immutable_metadata_hex, immutable_metadata_file).await?, - bytes_from_hex_or_file(metadata_hex, metadata_file).await?, - tag, - sender, - issuer, - ) - .await + "accounts" => { + // List all accounts + let accounts = wallet.get_accounts().await?; + println!("INDEX\tALIAS"); + for account in accounts { + let details = &*account.details().await; + println!("{}\t{}", details.index(), details.alias()); + } } - AccountCommand::NewAddress => new_address_command(account).await, - AccountCommand::NodeInfo => node_info_command(account).await, - AccountCommand::Output { output_id } => output_command(account, output_id).await, - AccountCommand::Outputs => outputs_command(account).await, - AccountCommand::Send { - address, - amount, - return_address, - expiration, - allow_micro_amount, - } => { - let allow_micro_amount = if return_address.is_some() || expiration.is_some() { - true - } else { - allow_micro_amount + _ => { + // Prepend `Account: ` so the parsing will be correct + let command = format!("Account: {}", command.trim()); + let account_cli = match AccountCli::try_parse_from(command.split_whitespace()) { + Ok(account_cli) => account_cli, + Err(err) => { + println!("{err}"); + return Ok(AccountPromptResponse::Reprompt); + } }; - send_command( - account, - address, - amount, - return_address, - expiration.map(|e| e.as_secs() as u32), - allow_micro_amount, - ) - .await + if let Err(err) = match account_cli.command { + AccountCommand::Addresses => addresses_command(account).await, + AccountCommand::Balance { addresses } => balance_command(account, addresses).await, + AccountCommand::BurnNativeToken { token_id, amount } => { + burn_native_token_command(account, token_id, amount).await + } + AccountCommand::BurnNft { nft_id } => burn_nft_command(account, nft_id).await, + AccountCommand::Claim { output_id } => claim_command(account, output_id).await, + AccountCommand::ClaimableOutputs => claimable_outputs_command(account).await, + AccountCommand::Consolidate => consolidate_command(account).await, + AccountCommand::CreateAliasOutput => create_alias_outputs_command(account).await, + AccountCommand::CreateNativeToken { + circulating_supply, + maximum_supply, + foundry_metadata_hex, + foundry_metadata_file, + } => { + create_native_token_command( + account, + circulating_supply, + maximum_supply, + bytes_from_hex_or_file(foundry_metadata_hex, foundry_metadata_file).await?, + ) + .await + } + AccountCommand::DestroyAlias { alias_id } => destroy_alias_command(account, alias_id).await, + AccountCommand::DestroyFoundry { foundry_id } => { + destroy_foundry_command(account, foundry_id).await + } + AccountCommand::Exit => { + return Ok(AccountPromptResponse::Done); + } + AccountCommand::Faucet { address, url } => faucet_command(account, address, url).await, + AccountCommand::MeltNativeToken { token_id, amount } => { + melt_native_token_command(account, token_id, amount).await + } + AccountCommand::MintNativeToken { token_id, amount } => { + mint_native_token(account, token_id, amount).await + } + AccountCommand::MintNft { + address, + immutable_metadata_hex, + immutable_metadata_file, + metadata_hex, + metadata_file, + tag, + sender, + issuer, + } => { + mint_nft_command( + account, + address, + bytes_from_hex_or_file(immutable_metadata_hex, immutable_metadata_file).await?, + bytes_from_hex_or_file(metadata_hex, metadata_file).await?, + tag, + sender, + issuer, + ) + .await + } + AccountCommand::NewAddress => new_address_command(account).await, + AccountCommand::NodeInfo => node_info_command(account).await, + AccountCommand::Output { output_id } => output_command(account, output_id).await, + AccountCommand::Outputs => outputs_command(account).await, + AccountCommand::Send { + address, + amount, + return_address, + expiration, + allow_micro_amount, + } => { + let allow_micro_amount = if return_address.is_some() || expiration.is_some() { + true + } else { + allow_micro_amount + }; + send_command( + account, + address, + amount, + return_address, + expiration.map(|e| e.as_secs() as u32), + allow_micro_amount, + ) + .await + } + AccountCommand::SendNativeToken { + address, + token_id, + amount, + gift_storage_deposit, + } => send_native_token_command(account, address, token_id, amount, gift_storage_deposit).await, + AccountCommand::SendNft { address, nft_id } => send_nft_command(account, address, nft_id).await, + AccountCommand::Switch { account_id } => { + return Ok(AccountPromptResponse::Switch(wallet.get_account(account_id).await?)); + } + AccountCommand::Sync => sync_command(account).await, + AccountCommand::Transaction { selector } => transaction_command(account, selector).await, + AccountCommand::Transactions { show_details } => { + transactions_command(account, show_details).await + } + AccountCommand::UnspentOutputs => unspent_outputs_command(account).await, + AccountCommand::Vote { event_id, answers } => vote_command(account, event_id, answers).await, + AccountCommand::StopParticipating { event_id } => { + stop_participating_command(account, event_id).await + } + AccountCommand::ParticipationOverview { event_ids } => { + let event_ids = (!event_ids.is_empty()).then_some(event_ids); + participation_overview_command(account, event_ids).await + } + AccountCommand::VotingPower => voting_power_command(account).await, + AccountCommand::IncreaseVotingPower { amount } => { + increase_voting_power_command(account, amount).await + } + AccountCommand::DecreaseVotingPower { amount } => { + decrease_voting_power_command(account, amount).await + } + AccountCommand::VotingOutput => voting_output_command(account).await, + } { + println_log_error!("{err}"); + } } - AccountCommand::SendNativeToken { - address, - token_id, - amount, - gift_storage_deposit, - } => send_native_token_command(account, address, token_id, amount, gift_storage_deposit).await, - AccountCommand::SendNft { address, nft_id } => send_nft_command(account, address, nft_id).await, - AccountCommand::Switch { account_id } => { - return Ok(AccountPromptResponse::Switch(wallet.get_account(account_id).await?)); - } - AccountCommand::Sync => sync_command(account).await, - AccountCommand::Transaction { selector } => transaction_command(account, selector).await, - AccountCommand::Transactions { show_details } => transactions_command(account, show_details).await, - AccountCommand::UnspentOutputs => unspent_outputs_command(account).await, - AccountCommand::Vote { event_id, answers } => vote_command(account, event_id, answers).await, - AccountCommand::StopParticipating { event_id } => stop_participating_command(account, event_id).await, - AccountCommand::ParticipationOverview { event_ids } => { - let event_ids = (!event_ids.is_empty()).then_some(event_ids); - participation_overview_command(account, event_ids).await - } - AccountCommand::VotingPower => voting_power_command(account).await, - AccountCommand::IncreaseVotingPower { amount } => increase_voting_power_command(account, amount).await, - AccountCommand::DecreaseVotingPower { amount } => decrease_voting_power_command(account, amount).await, - AccountCommand::VotingOutput => voting_output_command(account).await, - } { - println_log_error!("{err}"); } } + Err(ReadlineError::Interrupted) => { + return Ok(AccountPromptResponse::Done); + } + Err(_) => {} } Ok(AccountPromptResponse::Reprompt) diff --git a/cli/src/account_completion.rs b/cli/src/account_completion.rs index 98a000aae1..157ad5080e 100644 --- a/cli/src/account_completion.rs +++ b/cli/src/account_completion.rs @@ -1,11 +1,11 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use dialoguer::Completion; +use rustyline::{completion::Completer, Context}; -pub(crate) struct AccountCompletion; +pub struct AccountCompleter; -pub(crate) const ACCOUNT_COMPLETION: &[&str] = &[ +const ACCOUNT_COMMANDS: &[&str] = &[ "accounts", "addresses", "balance", @@ -47,17 +47,15 @@ pub(crate) const ACCOUNT_COMPLETION: &[&str] = &[ "help", ]; -impl Completion for AccountCompletion { - fn get(&self, input: &str) -> Option { - let matches = ACCOUNT_COMPLETION - .iter() - .filter(|option| option.starts_with(input)) - .collect::>(); - - if matches.len() == 1 { - Some(matches[0].to_string()) - } else { - None +impl Completer for AccountCompleter { + type Candidate = String; + fn complete(&self, input: &str, _pos: usize, _ctx: &Context<'_>) -> rustyline::Result<(usize, Vec)> { + let mut completions = vec![]; + for command in ACCOUNT_COMMANDS { + if command.starts_with(input) { + completions.push(command.to_string()); + } } + Ok((0, completions)) } } diff --git a/cli/src/main.rs b/cli/src/main.rs index a3f417f228..43eac08d6a 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -3,7 +3,6 @@ mod account; mod account_completion; -mod account_history; mod command; mod error; mod helper; From 1d5ca50043eaf8d5dea4e720efa5ebd2436d8acc Mon Sep 17 00:00:00 2001 From: Alexander Schmidt Date: Mon, 18 Sep 2023 09:31:24 +0200 Subject: [PATCH 03/10] rm cutom history impl --- cli/src/account_history.rs | 53 -------------------------------------- 1 file changed, 53 deletions(-) delete mode 100644 cli/src/account_history.rs diff --git a/cli/src/account_history.rs b/cli/src/account_history.rs deleted file mode 100644 index 37afabedaa..0000000000 --- a/cli/src/account_history.rs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2023 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::collections::VecDeque; - -use dialoguer::History; - -pub struct AccountHistory { - max: usize, - history: VecDeque, -} - -impl Default for AccountHistory { - fn default() -> Self { - AccountHistory { - max: 25, - history: VecDeque::new(), - } - } -} - -impl History for AccountHistory { - fn read(&self, pos: usize) -> Option { - self.history.get(pos).cloned() - } - - fn write(&mut self, val: &T) { - let entry = val.to_string(); - - // If the last used command is the same, don't change anything - if matches!(self.history.front(), Some(command) if command == &entry) { - return; - } - - // Check if we have used this command before - match self.history.iter().position(|e| e == &entry) { - Some(index) => { - // Remove the old command - self.history.remove(index); - } - None => { - // We have not used this command - if self.history.len() == self.max { - // Remove the oldest used command - self.history.pop_back(); - } - } - } - - // Add command as most recent used - self.history.push_front(entry); - } -} From 6f284b741701d325b411d795577f43d0d12f0be3 Mon Sep 17 00:00:00 2001 From: Alexander Schmidt Date: Mon, 18 Sep 2023 11:27:52 +0200 Subject: [PATCH 04/10] changelog --- cli/CHANGELOG.md | 1 + cli/Cargo.toml | 6 +----- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index fc2c6b59ac..db9b54a62e 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `addresses` now additionally prints the hex version of the address; - `outputs`, `unspent-outputs` print a list that includes number and type of the output; - `Account::switch` command to allow changing accounts quickly; +- UX improvements (Ctrl+l, TAB completion/suggestions and more) during interactive account management; ### Changed diff --git a/cli/Cargo.toml b/cli/Cargo.toml index bfea67e03a..c4b9d0845c 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -33,11 +33,7 @@ clap = { version = "4.4.2", default-features = false, features = [ "env", ] } colored = { version = "2.0.4", default-features = false } -dialoguer = { version = "0.10.4", default-features = false, features = [ - #"history", - "password", - #"completion", -] } +dialoguer = { version = "0.10.4", default-features = false, features = [ "password" ] } dotenvy = { version = "0.15.7", default-features = false } fern-logger = { version = "0.5.0", default-features = false } humantime = { version = "2.1.0", default-features = false } From 2182db1100b76374d29b56b055cd754a96f63d8c Mon Sep 17 00:00:00 2001 From: Alexander Schmidt Date: Mon, 18 Sep 2023 15:01:21 +0200 Subject: [PATCH 05/10] review 1 --- cli/Cargo.toml | 2 +- cli/src/account_completion.rs | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/cli/Cargo.toml b/cli/Cargo.toml index e2bf3bd23d..c7ef5e6de4 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -33,7 +33,7 @@ clap = { version = "4.4.2", default-features = false, features = [ "env", ] } colored = { version = "2.0.4", default-features = false } -dialoguer = { version = "0.10.4", default-features = false, features = [ "password" ] } +dialoguer = { version = "0.10.4", default-features = false, features = ["password"] } dotenvy = { version = "0.15.7", default-features = false } fern-logger = { version = "0.5.0", default-features = false } humantime = { version = "2.1.0", default-features = false } diff --git a/cli/src/account_completion.rs b/cli/src/account_completion.rs index 157ad5080e..e655653590 100644 --- a/cli/src/account_completion.rs +++ b/cli/src/account_completion.rs @@ -49,13 +49,14 @@ const ACCOUNT_COMMANDS: &[&str] = &[ impl Completer for AccountCompleter { type Candidate = String; + fn complete(&self, input: &str, _pos: usize, _ctx: &Context<'_>) -> rustyline::Result<(usize, Vec)> { - let mut completions = vec![]; - for command in ACCOUNT_COMMANDS { - if command.starts_with(input) { - completions.push(command.to_string()); - } - } - Ok((0, completions)) + Ok(( + 0, + ACCOUNT_COMMANDS + .iter() + .filter_map(|cmd| cmd.starts_with(input).then_some(cmd.to_string())) + .collect(), + )) } } From 70f2c4313ec8be573d6fb6dcb3230d4588555a27 Mon Sep 17 00:00:00 2001 From: Alexander Schmidt Date: Mon, 18 Sep 2023 15:52:39 +0200 Subject: [PATCH 06/10] review 2 --- cli/src/account.rs | 40 +++++++++++++++++++---------------- cli/src/account_completion.rs | 2 +- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/cli/src/account.rs b/cli/src/account.rs index 83d74c6ca6..be89001d49 100644 --- a/cli/src/account.rs +++ b/cli/src/account.rs @@ -51,6 +51,16 @@ impl Highlighter for PromptHelper { } } +impl Default for PromptHelper { + fn default() -> Self { + Self { + completer: AccountCompleter {}, + hinter: HistoryHinter {}, + colored_prompt: String::new(), + } + } +} + // loop on the account prompt pub async fn account_prompt(wallet: &Wallet, mut account: Account) -> Result<(), Error> { let config = Config::builder() @@ -60,15 +70,9 @@ pub async fn account_prompt(wallet: &Wallet, mut account: Account) -> Result<(), .edit_mode(rustyline::EditMode::Emacs) .build(); - // Panic: should never happen + // Panic: should never happen since it's all in-memory let mut rl = Editor::with_history(config, MemHistory::with_config(config)).unwrap(); - - let helper = PromptHelper { - completer: AccountCompleter, - hinter: HistoryHinter {}, - colored_prompt: String::new(), - }; - rl.set_helper(Some(helper)); + rl.set_helper(Some(PromptHelper::default())); loop { match account_prompt_internal(wallet, &account, &mut rl).await { @@ -100,17 +104,15 @@ pub async fn account_prompt_internal( account: &Account, rl: &mut Editor, ) -> Result { - let alias = { - let account = account.details().await; - account.alias().clone() - }; + let alias = account.details().await.alias().clone(); - let prompt = format!("Account \"{}\": ", alias); - // Panic: should never happen since there's always a helper - rl.helper_mut().unwrap().colored_prompt = prompt.green().to_string(); + let prompt = format!("Account \"{alias}\": "); + if let Some(helper) = rl.helper_mut() { + helper.colored_prompt = prompt.green().to_string(); + } - let command = rl.readline(&prompt); - match command { + let input = rl.readline(&prompt); + match input { Ok(command) => { match command.as_str() { "h" | "help" => AccountCli::print_help()?, @@ -264,7 +266,9 @@ pub async fn account_prompt_internal( Err(ReadlineError::Interrupted) => { return Ok(AccountPromptResponse::Done); } - Err(_) => {} + Err(err) => { + println_log_error!("{err}"); + } } Ok(AccountPromptResponse::Reprompt) diff --git a/cli/src/account_completion.rs b/cli/src/account_completion.rs index e655653590..1520b2e492 100644 --- a/cli/src/account_completion.rs +++ b/cli/src/account_completion.rs @@ -3,7 +3,7 @@ use rustyline::{completion::Completer, Context}; -pub struct AccountCompleter; +pub struct AccountCompleter {} const ACCOUNT_COMMANDS: &[&str] = &[ "accounts", From 7cee806d26d564e208d9255cfbe3a16fcc76836a Mon Sep 17 00:00:00 2001 From: Alexander Schmidt Date: Tue, 19 Sep 2023 10:01:29 +0200 Subject: [PATCH 07/10] review 3 --- cli/src/account.rs | 31 +++++++------ cli/src/command/account_completion.rs | 64 +++++++++++++++++++++++++++ cli/src/command/mod.rs | 1 + cli/src/main.rs | 1 - 4 files changed, 82 insertions(+), 15 deletions(-) create mode 100644 cli/src/command/account_completion.rs diff --git a/cli/src/account.rs b/cli/src/account.rs index be89001d49..11b1bb2715 100644 --- a/cli/src/account.rs +++ b/cli/src/account.rs @@ -12,16 +12,18 @@ use rustyline::{ }; use crate::{ - account_completion::AccountCompleter, - command::account::{ - addresses_command, balance_command, burn_native_token_command, burn_nft_command, claim_command, - claimable_outputs_command, consolidate_command, create_alias_outputs_command, create_native_token_command, - decrease_voting_power_command, destroy_alias_command, destroy_foundry_command, faucet_command, - increase_voting_power_command, melt_native_token_command, mint_native_token, mint_nft_command, - new_address_command, node_info_command, output_command, outputs_command, participation_overview_command, - send_command, send_native_token_command, send_nft_command, stop_participating_command, sync_command, - transaction_command, transactions_command, unspent_outputs_command, vote_command, voting_output_command, - voting_power_command, AccountCli, AccountCommand, + command::{ + account::{ + addresses_command, balance_command, burn_native_token_command, burn_nft_command, claim_command, + claimable_outputs_command, consolidate_command, create_alias_outputs_command, create_native_token_command, + decrease_voting_power_command, destroy_alias_command, destroy_foundry_command, faucet_command, + increase_voting_power_command, melt_native_token_command, mint_native_token, mint_nft_command, + new_address_command, node_info_command, output_command, outputs_command, participation_overview_command, + send_command, send_native_token_command, send_nft_command, stop_participating_command, sync_command, + transaction_command, transactions_command, unspent_outputs_command, vote_command, voting_output_command, + voting_power_command, AccountCli, AccountCommand, + }, + account_completion::AccountCompleter, }, error::Error, helper::bytes_from_hex_or_file, @@ -54,7 +56,7 @@ impl Highlighter for PromptHelper { impl Default for PromptHelper { fn default() -> Self { Self { - completer: AccountCompleter {}, + completer: AccountCompleter, hinter: HistoryHinter {}, colored_prompt: String::new(), } @@ -139,7 +141,7 @@ pub async fn account_prompt_internal( return Ok(AccountPromptResponse::Reprompt); } }; - if let Err(err) = match account_cli.command { + match account_cli.command { AccountCommand::Addresses => addresses_command(account).await, AccountCommand::Balance { addresses } => balance_command(account, addresses).await, AccountCommand::BurnNativeToken { token_id, amount } => { @@ -257,9 +259,10 @@ pub async fn account_prompt_internal( decrease_voting_power_command(account, amount).await } AccountCommand::VotingOutput => voting_output_command(account).await, - } { - println_log_error!("{err}"); } + .unwrap_or_else(|err| { + println_log_error!("{err}"); + }); } } } diff --git a/cli/src/command/account_completion.rs b/cli/src/command/account_completion.rs new file mode 100644 index 0000000000..a508142653 --- /dev/null +++ b/cli/src/command/account_completion.rs @@ -0,0 +1,64 @@ +// Copyright 2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use rustyline::{completion::Completer, Context}; + +#[derive(Default)] +pub struct AccountCompleter; + +const ACCOUNT_COMMANDS: &[&str] = &[ + "accounts", + "addresses", + "balance", + "burn-native-token", + "burn-nft", + "claim", + "claimable-outputs", + "clear", + "consolidate", + "create-alias-output", + "create-native-token", + "destroy-alias", + "destroy-foundry", + "exit", + "faucet", + "melt-native-token", + "mint-native-token", + "mint-nft", + "new-address", + "node-info", + "output", + "outputs", + "send", + "send-native-token", + "send-nft", + "switch", + "sync", + "transaction", + "transactions", + "tx", + "txs", + "unspent-outputs", + "vote", + "stop-participating", + "participation-overview", + "voting-power", + "increase-voting-power", + "decrease-voting-power", + "voting-output", + "help", +]; + +impl Completer for AccountCompleter { + type Candidate = String; + + fn complete(&self, input: &str, _pos: usize, _ctx: &Context<'_>) -> rustyline::Result<(usize, Vec)> { + Ok(( + 0, + ACCOUNT_COMMANDS + .iter() + .filter_map(|cmd| cmd.starts_with(input).then_some(cmd.to_string())) + .collect(), + )) + } +} diff --git a/cli/src/command/mod.rs b/cli/src/command/mod.rs index b67764ee45..503b26a487 100644 --- a/cli/src/command/mod.rs +++ b/cli/src/command/mod.rs @@ -2,4 +2,5 @@ // SPDX-License-Identifier: Apache-2.0 pub mod account; +pub mod account_completion; pub mod wallet; diff --git a/cli/src/main.rs b/cli/src/main.rs index 43eac08d6a..ea0336e05b 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 mod account; -mod account_completion; mod command; mod error; mod helper; From 85265851a64cb2bb025c0b8017b040ecffdd3830 Mon Sep 17 00:00:00 2001 From: Alexander Schmidt Date: Tue, 19 Sep 2023 10:10:54 +0200 Subject: [PATCH 08/10] review 4 --- cli/src/account.rs | 3 +- cli/src/account_completion.rs | 62 ----------------------------------- cli/src/error.rs | 3 ++ 3 files changed, 4 insertions(+), 64 deletions(-) delete mode 100644 cli/src/account_completion.rs diff --git a/cli/src/account.rs b/cli/src/account.rs index 11b1bb2715..88a1349cae 100644 --- a/cli/src/account.rs +++ b/cli/src/account.rs @@ -72,8 +72,7 @@ pub async fn account_prompt(wallet: &Wallet, mut account: Account) -> Result<(), .edit_mode(rustyline::EditMode::Emacs) .build(); - // Panic: should never happen since it's all in-memory - let mut rl = Editor::with_history(config, MemHistory::with_config(config)).unwrap(); + let mut rl = Editor::with_history(config, MemHistory::with_config(config))?; rl.set_helper(Some(PromptHelper::default())); loop { diff --git a/cli/src/account_completion.rs b/cli/src/account_completion.rs deleted file mode 100644 index 1520b2e492..0000000000 --- a/cli/src/account_completion.rs +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2023 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use rustyline::{completion::Completer, Context}; - -pub struct AccountCompleter {} - -const ACCOUNT_COMMANDS: &[&str] = &[ - "accounts", - "addresses", - "balance", - "burn-native-token", - "burn-nft", - "claim", - "claimable-outputs", - "consolidate", - "create-alias-output", - "create-native-token", - "destroy-alias", - "destroy-foundry", - "exit", - "faucet", - "melt-native-token", - "mint-native-token", - "mint-nft", - "new-address", - "node-info", - "output", - "outputs", - "send", - "send-native-token", - "send-nft", - "switch", - "sync", - "transaction", - "transactions", - "tx", - "txs", - "unspent-outputs", - "vote", - "stop-participating", - "participation-overview", - "voting-power", - "increase-voting-power", - "decrease-voting-power", - "voting-output", - "help", -]; - -impl Completer for AccountCompleter { - type Candidate = String; - - fn complete(&self, input: &str, _pos: usize, _ctx: &Context<'_>) -> rustyline::Result<(usize, Vec)> { - Ok(( - 0, - ACCOUNT_COMMANDS - .iter() - .filter_map(|cmd| cmd.starts_with(input).then_some(cmd.to_string())) - .collect(), - )) - } -} diff --git a/cli/src/error.rs b/cli/src/error.rs index f328062d54..bea7e656b2 100644 --- a/cli/src/error.rs +++ b/cli/src/error.rs @@ -5,6 +5,7 @@ use fern_logger::Error as LoggerError; use iota_sdk::{ client::error::Error as ClientError, types::block::Error as BlockError, wallet::error::Error as WalletError, }; +use rustyline::error::ReadlineError; use serde_json::Error as SerdeJsonError; #[derive(Debug, thiserror::Error)] @@ -21,6 +22,8 @@ pub enum Error { Miscellaneous(String), #[error("generate at least one address before using the faucet")] NoAddressForFaucet, + #[error("prompt error: {0}")] + Prompt(#[from] ReadlineError), #[error("serde_json error: {0}")] SerdeJson(#[from] SerdeJsonError), #[error("wallet error: {0}")] From 1205fd1a2568d035a40b01155789ec3b30ec5020 Mon Sep 17 00:00:00 2001 From: Alexander Schmidt Date: Tue, 19 Sep 2023 15:11:09 +0200 Subject: [PATCH 09/10] review 5 --- cli/src/account.rs | 48 +++------------------------ cli/src/command/account_completion.rs | 46 ++++++++++++++++++++++++- 2 files changed, 50 insertions(+), 44 deletions(-) diff --git a/cli/src/account.rs b/cli/src/account.rs index 88a1349cae..83f9d32644 100644 --- a/cli/src/account.rs +++ b/cli/src/account.rs @@ -1,15 +1,10 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use std::borrow::Cow; - use clap::Parser; use colored::Colorize; use iota_sdk::wallet::{Account, Wallet}; -use rustyline::{ - error::ReadlineError, highlight::Highlighter, hint::HistoryHinter, history::MemHistory, Completer, Config, Editor, - Helper, Hinter, Validator, -}; +use rustyline::{error::ReadlineError, history::MemHistory, Config, Editor}; use crate::{ command::{ @@ -23,46 +18,13 @@ use crate::{ transaction_command, transactions_command, unspent_outputs_command, vote_command, voting_output_command, voting_power_command, AccountCli, AccountCommand, }, - account_completion::AccountCompleter, + account_completion::AccountPromptHelper, }, error::Error, helper::bytes_from_hex_or_file, println_log_error, }; -#[derive(Helper, Completer, Hinter, Validator)] -pub struct PromptHelper { - #[rustyline(Completer)] - completer: AccountCompleter, - #[rustyline(Hinter)] - hinter: HistoryHinter, - colored_prompt: String, -} - -impl Highlighter for PromptHelper { - fn highlight_prompt<'b, 's: 'b, 'p: 'b>(&'s self, prompt: &'p str, default: bool) -> Cow<'b, str> { - if default { - Cow::Borrowed(&self.colored_prompt) - } else { - Cow::Borrowed(prompt) - } - } - - fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> { - Cow::Owned(hint.bold().to_string()) - } -} - -impl Default for PromptHelper { - fn default() -> Self { - Self { - completer: AccountCompleter, - hinter: HistoryHinter {}, - colored_prompt: String::new(), - } - } -} - // loop on the account prompt pub async fn account_prompt(wallet: &Wallet, mut account: Account) -> Result<(), Error> { let config = Config::builder() @@ -73,7 +35,7 @@ pub async fn account_prompt(wallet: &Wallet, mut account: Account) -> Result<(), .build(); let mut rl = Editor::with_history(config, MemHistory::with_config(config))?; - rl.set_helper(Some(PromptHelper::default())); + rl.set_helper(Some(AccountPromptHelper::default())); loop { match account_prompt_internal(wallet, &account, &mut rl).await { @@ -103,13 +65,13 @@ pub enum AccountPromptResponse { pub async fn account_prompt_internal( wallet: &Wallet, account: &Account, - rl: &mut Editor, + rl: &mut Editor, ) -> Result { let alias = account.details().await.alias().clone(); let prompt = format!("Account \"{alias}\": "); if let Some(helper) = rl.helper_mut() { - helper.colored_prompt = prompt.green().to_string(); + helper.set_prompt(prompt.green().to_string()); } let input = rl.readline(&prompt); diff --git a/cli/src/command/account_completion.rs b/cli/src/command/account_completion.rs index a508142653..05bdac39c3 100644 --- a/cli/src/command/account_completion.rs +++ b/cli/src/command/account_completion.rs @@ -1,7 +1,12 @@ // Copyright 2023 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use rustyline::{completion::Completer, Context}; +use std::borrow::Cow; + +use colored::Colorize; +use rustyline::{ + completion::Completer, highlight::Highlighter, hint::HistoryHinter, Completer, Context, Helper, Hinter, Validator, +}; #[derive(Default)] pub struct AccountCompleter; @@ -62,3 +67,42 @@ impl Completer for AccountCompleter { )) } } + +#[derive(Helper, Completer, Hinter, Validator)] +pub struct AccountPromptHelper { + #[rustyline(Completer)] + completer: AccountCompleter, + #[rustyline(Hinter)] + hinter: HistoryHinter, + prompt: String, +} + +impl AccountPromptHelper { + pub fn set_prompt(&mut self, prompt: String) { + self.prompt = prompt; + } +} + +impl Highlighter for AccountPromptHelper { + fn highlight_prompt<'b, 's: 'b, 'p: 'b>(&'s self, prompt: &'p str, default: bool) -> Cow<'b, str> { + if default { + Cow::Borrowed(&self.prompt) + } else { + Cow::Borrowed(prompt) + } + } + + fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> { + Cow::Owned(hint.bold().to_string()) + } +} + +impl Default for AccountPromptHelper { + fn default() -> Self { + Self { + completer: AccountCompleter, + hinter: HistoryHinter {}, + prompt: String::new(), + } + } +} From 8921531a2212e8649b1fe942f0f3f43eee48c7e9 Mon Sep 17 00:00:00 2001 From: Alexander Schmidt Date: Tue, 19 Sep 2023 22:38:25 +0200 Subject: [PATCH 10/10] &'static str --- cli/src/command/account_completion.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/cli/src/command/account_completion.rs b/cli/src/command/account_completion.rs index 05bdac39c3..53a78c1261 100644 --- a/cli/src/command/account_completion.rs +++ b/cli/src/command/account_completion.rs @@ -55,14 +55,19 @@ const ACCOUNT_COMMANDS: &[&str] = &[ ]; impl Completer for AccountCompleter { - type Candidate = String; + type Candidate = &'static str; - fn complete(&self, input: &str, _pos: usize, _ctx: &Context<'_>) -> rustyline::Result<(usize, Vec)> { + fn complete( + &self, + input: &str, + _pos: usize, + _ctx: &Context<'_>, + ) -> rustyline::Result<(usize, Vec)> { Ok(( 0, ACCOUNT_COMMANDS .iter() - .filter_map(|cmd| cmd.starts_with(input).then_some(cmd.to_string())) + .filter_map(|cmd| cmd.starts_with(input).then_some(*cmd)) .collect(), )) }