From 62112e5c73ee604c1f841cece9e77e63177f8160 Mon Sep 17 00:00:00 2001 From: Paolo Barbolini Date: Mon, 6 Jan 2025 17:40:48 +0100 Subject: [PATCH] refactor(cli): replace promptly with dialoguer --- Cargo.lock | 158 +++++---------------------------------- sqlx-cli/Cargo.toml | 4 +- sqlx-cli/src/database.rs | 65 ++++++++++------ sqlx-cli/src/lib.rs | 16 ++++ 4 files changed, 78 insertions(+), 165 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 68f3c84cd8..045ca8f055 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -854,17 +854,6 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" -[[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 = "cmake" version = "0.1.50" @@ -1130,6 +1119,17 @@ dependencies = [ "serde", ] +[[package]] +name = "dialoguer" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de" +dependencies = [ + "console", + "shell-words", + "thiserror 1.0.58", +] + [[package]] name = "difflib" version = "0.4.0" @@ -1148,27 +1148,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "dirs-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" -dependencies = [ - "cfg-if", - "dirs-sys-next", -] - -[[package]] -name = "dirs-sys-next" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - [[package]] name = "displaydoc" version = "0.2.5" @@ -1219,12 +1198,6 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" -[[package]] -name = "endian-type" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" - [[package]] name = "env_filter" version = "0.1.0" @@ -1264,16 +1237,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[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 = "etcetera" version = "0.8.0" @@ -1348,17 +1311,6 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" -[[package]] -name = "fd-lock" -version = "3.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5" -dependencies = [ - "cfg-if", - "rustix 0.38.31", - "windows-sys 0.48.0", -] - [[package]] name = "filetime" version = "0.2.23" @@ -2135,17 +2087,6 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" -[[package]] -name = "libredox" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" -dependencies = [ - "bitflags 2.4.2", - "libc", - "redox_syscall", -] - [[package]] name = "libsqlite3-sys" version = "0.30.1" @@ -2329,15 +2270,6 @@ dependencies = [ "tempfile", ] -[[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.23.2" @@ -2810,15 +2742,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "promptly" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9acbc6c5a5b029fe58342f58445acb00ccfe24624e538894bc2f04ce112980ba" -dependencies = [ - "rustyline", -] - [[package]] name = "ptr_meta" version = "0.1.4" @@ -2854,16 +2777,6 @@ 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" @@ -2953,17 +2866,6 @@ dependencies = [ "bitflags 1.3.2", ] -[[package]] -name = "redox_users" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" -dependencies = [ - "getrandom", - "libredox", - "thiserror 1.0.58", -] - [[package]] name = "regex" version = "1.10.3" @@ -3183,30 +3085,6 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" -[[package]] -name = "rustyline" -version = "9.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db7826789c0e25614b03e5a54a0717a86f9ff6e6e5247f92b369472869320039" -dependencies = [ - "bitflags 1.3.2", - "cfg-if", - "clipboard-win", - "dirs-next", - "fd-lock", - "libc", - "log", - "memchr", - "nix", - "radix_trie", - "scopeguard", - "smallvec", - "unicode-segmentation", - "unicode-width", - "utf8parse", - "winapi", -] - [[package]] name = "ryu" version = "1.0.17" @@ -3368,6 +3246,12 @@ dependencies = [ "digest", ] +[[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.3.0" @@ -3526,12 +3410,12 @@ dependencies = [ "clap", "clap_complete", "console", + "dialoguer", "dotenvy", "filetime", "futures", "glob", "openssl", - "promptly", "serde_json", "sqlx", "tempfile", @@ -3898,12 +3782,6 @@ 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 = "stringprep" version = "0.1.4" diff --git a/sqlx-cli/Cargo.toml b/sqlx-cli/Cargo.toml index 0b047ab136..3a5aefe5f4 100644 --- a/sqlx-cli/Cargo.toml +++ b/sqlx-cli/Cargo.toml @@ -26,7 +26,7 @@ path = "src/bin/cargo-sqlx.rs" [dependencies] dotenvy = "0.15.0" -tokio = { version = "1.15.0", features = ["macros", "rt", "rt-multi-thread"] } +tokio = { version = "1.15.0", features = ["macros", "rt", "rt-multi-thread", "signal"] } sqlx = { workspace = true, default-features = false, features = [ "runtime-tokio", "migrate", @@ -39,7 +39,7 @@ chrono = { version = "0.4.19", default-features = false, features = ["clock"] } anyhow = "1.0.52" async-trait = "0.1.52" console = "0.15.0" -promptly = "0.3.0" +dialoguer = { version = "0.11", default-features = false } serde_json = "1.0.73" glob = "0.3.0" openssl = { version = "0.10.38", optional = true } diff --git a/sqlx-cli/src/database.rs b/sqlx-cli/src/database.rs index 7a2056ab35..694b3ffff4 100644 --- a/sqlx-cli/src/database.rs +++ b/sqlx-cli/src/database.rs @@ -1,9 +1,11 @@ use crate::migrate; use crate::opt::ConnectOpts; -use console::style; -use promptly::{prompt, ReadlineError}; +use console::{style, Term}; +use dialoguer::Confirm; use sqlx::any::Any; use sqlx::migrate::MigrateDatabase; +use std::{io, mem}; +use tokio::task; pub async fn create(connect_opts: &ConnectOpts) -> anyhow::Result<()> { // NOTE: only retry the idempotent action. @@ -24,7 +26,7 @@ pub async fn create(connect_opts: &ConnectOpts) -> anyhow::Result<()> { } pub async fn drop(connect_opts: &ConnectOpts, confirm: bool, force: bool) -> anyhow::Result<()> { - if confirm && !ask_to_continue_drop(connect_opts.required_db_url()?) { + if confirm && !ask_to_continue_drop(connect_opts.required_db_url()?.to_owned()).await { return Ok(()); } @@ -58,27 +60,44 @@ pub async fn setup(migration_source: &str, connect_opts: &ConnectOpts) -> anyhow migrate::run(migration_source, connect_opts, false, false, None).await } -fn ask_to_continue_drop(db_url: &str) -> bool { - loop { - let r: Result = - prompt(format!("Drop database at {}? (y/n)", style(db_url).cyan())); - match r { - Ok(response) => { - if response == "n" || response == "N" { - return false; - } else if response == "y" || response == "Y" { - return true; - } else { - println!( - "Response not recognized: {}\nPlease type 'y' or 'n' and press enter.", - response - ); - } - } - Err(e) => { - println!("{e}"); - return false; +async fn ask_to_continue_drop(db_url: String) -> bool { + struct RestoreCursorGuard { + disarmed: bool, + } + + impl Drop for RestoreCursorGuard { + fn drop(&mut self) { + if !self.disarmed { + Term::stderr().show_cursor().unwrap() } } } + + let mut guard = RestoreCursorGuard { disarmed: false }; + + let decision_result = task::spawn_blocking(move || { + Confirm::new() + .with_prompt(format!("Drop database at {}?", style(&db_url).cyan())) + .wait_for_newline(true) + .default(false) + .show_default(true) + .interact() + }) + .await + .expect("Confirm thread panicked"); + match decision_result { + Ok(decision) => { + guard.disarmed = true; + decision + } + Err(dialoguer::Error::IO(err)) if err.kind() == io::ErrorKind::Interrupted => { + // Sometimes CTRL + C causes this error to be returned + mem::drop(guard); + false + } + Err(err) => { + mem::drop(guard); + panic!("Confirm dialog failed with {err}") + } + } } diff --git a/sqlx-cli/src/lib.rs b/sqlx-cli/src/lib.rs index bfd71e4bc1..6d91b21bd4 100644 --- a/sqlx-cli/src/lib.rs +++ b/sqlx-cli/src/lib.rs @@ -5,6 +5,7 @@ use anyhow::Result; use futures::{Future, TryFutureExt}; use sqlx::{AnyConnection, Connection}; +use tokio::{select, signal}; use crate::opt::{Command, ConnectOpts, DatabaseCommand, MigrateCommand}; @@ -21,6 +22,21 @@ mod prepare; pub use crate::opt::Opt; pub async fn run(opt: Opt) -> Result<()> { + let ctrlc_fut = signal::ctrl_c(); + let do_run_fut = do_run(opt); + + select! { + biased; + _ = ctrlc_fut => { + Ok(()) + }, + do_run_outcome = do_run_fut => { + do_run_outcome + } + } +} + +async fn do_run(opt: Opt) -> Result<()> { match opt.command { Command::Migrate(migrate) => match migrate.command { MigrateCommand::Add {