From 9d812580557cdc343378816cd65678b8aa75d944 Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Thu, 12 Mar 2026 15:54:59 +0800 Subject: Add lang field to command context and reorganize utils modules --- utils/src/input/confirm.rs | 52 ++++++++++++++++++++++++++++++++++++ utils/src/input/editor.rs | 66 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 utils/src/input/confirm.rs create mode 100644 utils/src/input/editor.rs (limited to 'utils/src/input') diff --git a/utils/src/input/confirm.rs b/utils/src/input/confirm.rs new file mode 100644 index 0000000..91d91a7 --- /dev/null +++ b/utils/src/input/confirm.rs @@ -0,0 +1,52 @@ +use tokio::io::{self, AsyncBufReadExt, AsyncWriteExt, BufReader}; + +/// Confirm the current operation +/// Waits for user input of 'y' or 'n' +pub async fn confirm_hint(text: impl Into) -> bool { + let prompt = text.into().trim().to_string(); + + let mut stdout = io::stdout(); + let mut stdin = BufReader::new(io::stdin()); + + stdout + .write_all(prompt.as_bytes()) + .await + .expect("Failed to write prompt"); + stdout.flush().await.expect("Failed to flush stdout"); + + let mut input = String::new(); + stdin + .read_line(&mut input) + .await + .expect("Failed to read input"); + + input.trim().eq_ignore_ascii_case("y") +} + +/// Confirm the current operation, or execute a closure if rejected +/// Waits for user input of 'y' or 'n' +/// If 'n' is entered, executes the provided closure and returns false +pub async fn confirm_hint_or(text: impl Into, on_reject: F) -> bool +where + F: FnOnce(), +{ + let confirmed = confirm_hint(text).await; + if !confirmed { + on_reject(); + } + confirmed +} + +/// Confirm the current operation, and execute a closure if confirmed +/// Waits for user input of 'y' or 'n' +/// If 'y' is entered, executes the provided closure and returns true +pub async fn confirm_hint_then(text: impl Into, on_confirm: F) -> bool +where + F: FnOnce(), +{ + let confirmed = confirm_hint(text).await; + if confirmed { + on_confirm(); + } + confirmed +} diff --git a/utils/src/input/editor.rs b/utils/src/input/editor.rs new file mode 100644 index 0000000..34377fd --- /dev/null +++ b/utils/src/input/editor.rs @@ -0,0 +1,66 @@ +use tokio::{fs, process::Command}; + +use crate::env::editor::get_default_editor; + +/// Input text using the system editor +/// Opens the system editor (from EDITOR environment variable) with default text in a cache file, +/// then reads back the modified content after the editor closes, removing comment lines +pub async fn input_with_editor( + default_text: impl AsRef, + cache_file: impl AsRef, + comment_char: impl AsRef, +) -> Result { + input_with_editor_cutsom( + default_text, + cache_file, + comment_char, + get_default_editor().await, + ) + .await +} + +pub async fn input_with_editor_cutsom( + default_text: impl AsRef, + cache_file: impl AsRef, + comment_char: impl AsRef, + editor: String, +) -> Result { + let cache_path = cache_file.as_ref(); + let default_content = default_text.as_ref(); + let comment_prefix = comment_char.as_ref(); + + // Write default text to cache file + fs::write(cache_path, default_content).await?; + + // Open editor with cache file + let status = Command::new(editor).arg(cache_path).status().await?; + + if !status.success() { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + "Editor exited with non-zero status", + )); + } + + // Read the modified content + let content = fs::read_to_string(cache_path).await?; + + // Remove comment lines and trim + let processed_content: String = content + .lines() + .filter_map(|line| { + let trimmed = line.trim(); + if trimmed.starts_with(comment_prefix) { + None + } else { + Some(line) + } + }) + .collect::>() + .join("\n"); + + // Delete the cache file + let _ = fs::remove_file(cache_path).await; + + Ok(processed_content) +} -- cgit