diff --git a/crates/cli/src/commands/tool/info.rs b/crates/cli/src/commands/tool/info.rs index 52101c42c..67283aa5f 100644 --- a/crates/cli/src/commands/tool/info.rs +++ b/crates/cli/src/commands/tool/info.rs @@ -1,10 +1,9 @@ -use super::list_plugins::print_locator; +use crate::printer::Printer; use clap::Args; use proto_core::{detect_version, load_tool, Id, PluginLocator}; use serde::Serialize; use starbase::system; -use starbase_styles::color::{self, OwoStyle}; -use std::io::{BufWriter, Write}; +use starbase_styles::color; #[derive(Serialize)] pub struct PluginItem { @@ -29,55 +28,31 @@ pub async fn tool_info(args: ArgsRef) { tool.create_executables(false, false).await?; tool.locate_globals_dir().await?; - let stdout = std::io::stdout(); - let mut buffer = BufWriter::new(stdout.lock()); - - writeln!( - buffer, - "{} {} {}", - OwoStyle::new().bold().style(color::id(&tool.id)), - color::muted("-"), - color::muted_light(&tool.metadata.name), - ) - .unwrap(); - - writeln!( - buffer, - "\n{}", - OwoStyle::new() - .bold() - .style(color::muted_light("Inventory")) - ) - .unwrap(); - - writeln!(buffer, " Store: {}", color::path(tool.get_inventory_dir())).unwrap(); - writeln!( - buffer, - " Executable: {}", - color::path(tool.get_exe_path()?) - ) - .unwrap(); + let mut printer = Printer::new(); + + printer.header(&tool.id, &tool.metadata.name); + + // INVENTORY + + printer.start_section("Inventory"); + + printer.entry("Store", color::path(tool.get_inventory_dir())); + + printer.entry("Executable", color::path(tool.get_exe_path()?)); if let Some(dir) = tool.get_globals_bin_dir() { - writeln!(buffer, " Globals directory: {}", color::path(dir)).unwrap(); + printer.entry("Globals directory", color::path(dir)); } if let Some(prefix) = tool.get_globals_prefix() { - writeln!(buffer, " Globals prefix: {}", color::property(prefix)).unwrap(); + printer.entry("Globals prefix", color::property(prefix)); } - let bins = tool.get_bin_locations()?; - - if bins.is_empty() { - writeln!(buffer, " Binaries: {}", color::failure("None")).unwrap(); - } else { - writeln!(buffer, " Binaries:").unwrap(); - - for bin in bins { - writeln!( - buffer, - " {} {} {}", - color::muted("-"), + printer.entry_list( + "Binaries", + tool.get_bin_locations()?.into_iter().map(|bin| { + format!( + "{} {}", color::path(bin.path), if bin.primary { color::muted_light("(primary)") @@ -85,22 +60,15 @@ pub async fn tool_info(args: ArgsRef) { "".into() } ) - .unwrap(); - } - } - - let shims = tool.get_shim_locations()?; - - if shims.is_empty() { - writeln!(buffer, " Shims: {}", color::failure("None")).unwrap(); - } else { - writeln!(buffer, " Shims:").unwrap(); - - for shim in shims { - writeln!( - buffer, - " {} {} {}", - color::muted("-"), + }), + color::failure("None"), + ); + + printer.entry_list( + "Shims", + tool.get_shim_locations()?.into_iter().map(|shim| { + format!( + "{} {}", color::path(shim.path), if shim.primary { color::muted_light("(primary)") @@ -108,26 +76,23 @@ pub async fn tool_info(args: ArgsRef) { "".into() } ) - .unwrap(); - } - } + }), + color::failure("None"), + ); - writeln!( - buffer, - "\n{}", - OwoStyle::new().bold().style(color::muted_light("Plugin")) - ) - .unwrap(); + printer.end_section(); + + // PLUGIN + + printer.start_section("Plugin"); if let Some(version) = &tool.metadata.plugin_version { - writeln!(buffer, " Version: {}", color::hash(version)).unwrap(); + printer.entry("Version", color::hash(version)); } - if let Some(locator) = &tool.locator { - print_locator(&mut buffer, locator); - } + printer.locator(tool.locator.as_ref().unwrap()); - writeln!(buffer, "").unwrap(); + printer.end_section(); - buffer.flush().unwrap(); + printer.flush(); } diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 3c83c522a..8eb92f7ea 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -3,6 +3,7 @@ mod commands; mod error; mod helpers; mod shell; +mod printer; use app::{App as CLI, Commands, ToolCommands}; use clap::Parser; diff --git a/crates/cli/src/printer.rs b/crates/cli/src/printer.rs new file mode 100644 index 000000000..666e20309 --- /dev/null +++ b/crates/cli/src/printer.rs @@ -0,0 +1,127 @@ +use proto_core::PluginLocator; +use starbase_styles::color::{self, OwoStyle}; +use std::io::{BufWriter, StdoutLock, Write}; + +pub struct Printer<'std> { + buffer: BufWriter>, + indent: u8, +} + +impl<'std> Printer<'std> { + pub fn new() -> Self { + let stdout = std::io::stdout(); + let buffer = BufWriter::new(stdout.lock()); + + Printer { buffer, indent: 0 } + } + + pub fn flush(&mut self) { + write!(&mut self.buffer, "\n").unwrap(); + + self.buffer.flush().unwrap(); + } + + // pub fn print>(&mut self, value: T) { + // self.indent(); + + // writeln!(&mut self.buffer, "{}", value.as_ref()).unwrap(); + // } + + pub fn header, V: AsRef>(&mut self, id: K, name: V) { + self.indent(); + + writeln!( + &mut self.buffer, + "{} {} {}", + OwoStyle::new().bold().style(color::id(id.as_ref())), + color::muted("-"), + color::muted_light(name.as_ref()), + ) + .unwrap(); + } + + pub fn start_section>(&mut self, header: T) { + write!(&mut self.buffer, "\n",).unwrap(); + + self.indent(); + + writeln!( + &mut self.buffer, + "{}", + OwoStyle::new() + .bold() + .style(color::muted_light(header.as_ref())) + ) + .unwrap(); + + self.indent += 1; + } + + pub fn end_section(&mut self) { + self.indent -= 1; + } + + pub fn indent(&mut self) { + if self.indent > 0 { + write!(&mut self.buffer, "{}", " ".repeat(self.indent as usize)).unwrap(); + } + } + + pub fn entry, V: AsRef>(&mut self, key: K, value: V) { + self.indent(); + + writeln!(&mut self.buffer, "{}: {}", key.as_ref(), value.as_ref()).unwrap(); + } + + pub fn entry_list, I: IntoIterator, V: AsRef, F: AsRef>( + &mut self, + key: K, + list: I, + empty: F, + ) { + let items = list.into_iter().collect::>(); + + if items.is_empty() { + self.entry(key, empty); + } else { + self.indent(); + + writeln!(&mut self.buffer, "{}:", key.as_ref()).unwrap(); + + self.indent += 1; + + for item in items { + self.indent(); + + writeln!(&mut self.buffer, "{} {}", color::muted("-"), item.as_ref()).unwrap(); + } + + self.indent -= 1; + } + } + + pub fn locator>(&mut self, locator: L) { + match locator.as_ref() { + PluginLocator::SourceFile { path, .. } => { + self.entry("Source", color::path(path.canonicalize().unwrap())); + } + PluginLocator::SourceUrl { url } => { + self.entry("Source", color::url(url)); + } + PluginLocator::GitHub(github) => { + self.entry("GitHub", color::label(&github.repo_slug)); + self.entry( + "Tag", + color::hash(github.tag.as_deref().unwrap_or("latest")), + ); + } + PluginLocator::Wapm(wapm) => { + self.entry("Package", color::label(&wapm.package_name)); + self.entry( + "Release", + color::hash(wapm.version.as_deref().unwrap_or("latest")), + ); + } + }; + } +}