From 46845eff8d1398f35099a0ef914f77bcaf473287 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sun, 10 May 2026 15:29:19 +0900 Subject: refactor(io): introduce IoInterface trait mirroring Composer IOInterface Add an `IoInterface` trait in mozart-core::console that mirrors `\Composer\IO\IOInterface`, implement it for `Console`, and switch commands, the auditor, and the suggested-packages reporter to accept the abstracted IO (typically `Arc>>` at the command boundary, `&dyn IoInterface` deeper down) instead of `&Console`. The console_writeln\!/write\! macros now go through `IoInterface::verbosity()` via the lock so any implementor works. --- crates/mozart-core/src/console.rs | 194 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 184 insertions(+), 10 deletions(-) (limited to 'crates/mozart-core/src/console.rs') diff --git a/crates/mozart-core/src/console.rs b/crates/mozart-core/src/console.rs index e036b11..3379307 100644 --- a/crates/mozart-core/src/console.rs +++ b/crates/mozart-core/src/console.rs @@ -66,6 +66,46 @@ pub fn hyperlink(url: &str, text: &str, decorated: bool) -> String { format!("\x1b]8;;{safe_url}\x1b\\{text}\x1b]8;;\x1b\\") } +// --------------------------------------------------------------------------- +// IoInterface +// --------------------------------------------------------------------------- + +/// The central IO abstraction, mirroring `\Composer\IO\IOInterface`. +/// +/// All Mozart commands and library functions that need to produce output +/// or interact with the user accept `&dyn IoInterface` (or an +/// `Arc>>` at the top-level command boundary). +pub trait IoInterface: Send + Sync { + fn write(&self, msg: &str, required: Verbosity); + fn write_stdout(&self, msg: &str, required: Verbosity); + fn write_error(&self, msg: &str); + + fn info(&self, msg: &str); + fn verbose(&self, msg: &str); + fn very_verbose(&self, msg: &str); + fn debug(&self, msg: &str); + fn error(&self, msg: &str); + + fn is_interactive(&self) -> bool; + fn is_decorated(&self) -> bool; + fn verbosity(&self) -> Verbosity; + + fn is_verbose(&self) -> bool; + fn is_very_verbose(&self) -> bool; + fn is_debug(&self) -> bool; + fn is_quiet(&self) -> bool; + + fn ask(&self, prompt: &str, default: &str) -> String; + #[allow(clippy::type_complexity)] + fn ask_validated( + &self, + prompt: &str, + default: &str, + validator: Box Result<(), String>>, + ) -> Result; + fn confirm(&self, prompt: &str) -> bool; +} + // --------------------------------------------------------------------------- // Verbosity // --------------------------------------------------------------------------- @@ -212,6 +252,18 @@ impl Console { // Query methods // ----------------------------------------------------------------------- + pub fn verbosity(&self) -> Verbosity { + self.verbosity + } + + pub fn is_interactive(&self) -> bool { + self.interactive + } + + pub fn is_decorated(&self) -> bool { + self.decorated + } + pub fn is_verbose(&self) -> bool { self.verbosity >= Verbosity::Verbose } @@ -245,15 +297,13 @@ impl Console { .unwrap_or_else(|_| default.to_string()) } - pub fn ask_validated( + #[allow(clippy::type_complexity)] + pub fn ask_validated( &self, prompt: &str, default: &str, - validator: F, - ) -> Result - where - F: Fn(&str) -> Result<(), String>, - { + validator: Box Result<(), String>>, + ) -> Result { if !self.interactive { validator(default)?; return Ok(default.to_string()); @@ -289,6 +339,130 @@ impl Console { } } +impl IoInterface for Box { + fn write(&self, msg: &str, required: Verbosity) { + (**self).write(msg, required) + } + fn write_stdout(&self, msg: &str, required: Verbosity) { + (**self).write_stdout(msg, required) + } + fn write_error(&self, msg: &str) { + (**self).write_error(msg) + } + fn info(&self, msg: &str) { + (**self).info(msg) + } + fn verbose(&self, msg: &str) { + (**self).verbose(msg) + } + fn very_verbose(&self, msg: &str) { + (**self).very_verbose(msg) + } + fn debug(&self, msg: &str) { + (**self).debug(msg) + } + fn error(&self, msg: &str) { + (**self).error(msg) + } + fn is_interactive(&self) -> bool { + (**self).is_interactive() + } + fn is_decorated(&self) -> bool { + (**self).is_decorated() + } + fn verbosity(&self) -> Verbosity { + (**self).verbosity() + } + fn is_verbose(&self) -> bool { + (**self).is_verbose() + } + fn is_very_verbose(&self) -> bool { + (**self).is_very_verbose() + } + fn is_debug(&self) -> bool { + (**self).is_debug() + } + fn is_quiet(&self) -> bool { + (**self).is_quiet() + } + fn ask(&self, prompt: &str, default: &str) -> String { + (**self).ask(prompt, default) + } + fn ask_validated( + &self, + prompt: &str, + default: &str, + validator: Box Result<(), String>>, + ) -> Result { + (**self).ask_validated(prompt, default, validator) + } + fn confirm(&self, prompt: &str) -> bool { + (**self).confirm(prompt) + } +} + +impl IoInterface for Console { + fn write(&self, msg: &str, required: Verbosity) { + self.write(msg, required) + } + fn write_stdout(&self, msg: &str, required: Verbosity) { + self.write_stdout(msg, required) + } + fn write_error(&self, msg: &str) { + self.write_error(msg) + } + fn info(&self, msg: &str) { + self.info(msg) + } + fn verbose(&self, msg: &str) { + self.verbose(msg) + } + fn very_verbose(&self, msg: &str) { + self.very_verbose(msg) + } + fn debug(&self, msg: &str) { + self.debug(msg) + } + fn error(&self, msg: &str) { + self.error(msg) + } + fn is_interactive(&self) -> bool { + self.is_interactive() + } + fn is_decorated(&self) -> bool { + self.is_decorated() + } + fn verbosity(&self) -> Verbosity { + self.verbosity() + } + fn is_verbose(&self) -> bool { + self.is_verbose() + } + fn is_very_verbose(&self) -> bool { + self.is_very_verbose() + } + fn is_debug(&self) -> bool { + self.is_debug() + } + fn is_quiet(&self) -> bool { + self.is_quiet() + } + fn ask(&self, prompt: &str, default: &str) -> String { + self.ask(prompt, default) + } + fn ask_validated( + &self, + prompt: &str, + default: &str, + validator: Box Result<(), String>>, + ) -> Result { + self.ask_validated(prompt, default, validator) + } + fn confirm(&self, prompt: &str) -> bool { + self.confirm(prompt) + } +} + /// Writes a message to the output. /// /// ref: \Composer\IO\IOInterface::write() @@ -304,7 +478,7 @@ macro_rules! console_writeln { $crate::console_writeln!($console, $verbosity, $fmt, $($arg)*,) }; ($console:expr, $verbosity:expr, $fmt:literal, $($arg:tt)*) => { - if ($console).verbosity >= $verbosity { + if $console.lock().unwrap().verbosity() >= $verbosity { ::std::println!("{}", &::mozart_console_macros::console_format!($fmt, $($arg)*)); } }; @@ -325,7 +499,7 @@ macro_rules! console_write { $crate::console_writeln!($console, $verbosity, $fmt, $($arg)*,) }; ($console:expr, $verbosity:expr, $fmt:literal, $($arg:tt)*) => { - if ($console).verbosity >= $verbosity { + if $console.lock().unwrap().verbosity() >= $verbosity { ::std::print!("{}", &::mozart_console_macros::console_format!($fmt, $($arg)*)); } }; @@ -346,7 +520,7 @@ macro_rules! console_writeln_error { $crate::console_writeln!($console, $verbosity, $fmt, $($arg)*,) }; ($console:expr, $verbosity:expr, $fmt:literal, $($arg:tt)*) => { - if ($console).verbosity >= $verbosity { + if $console.lock().unwrap().verbosity() >= $verbosity { ::std::eprintln!("{}", &::mozart_console_macros::console_format!($fmt, $($arg)*)); } }; @@ -367,7 +541,7 @@ macro_rules! console_write_error { $crate::console_writeln!($console, $verbosity, $fmt, $($arg)*,) }; ($console:expr, $verbosity:expr, $fmt:literal, $($arg:tt)*) => { - if ($console).verbosity >= $verbosity { + if $console.lock().unwrap().verbosity() >= $verbosity { ::std::eprint!("{}", &::mozart_console_macros::console_format!($fmt, $($arg)*)); } }; -- cgit v1.3.1