diff --git a/src/sync.rs b/src/sync.rs index 4d22754..6f0e67e 100644 --- a/src/sync.rs +++ b/src/sync.rs @@ -32,7 +32,12 @@ For more details, refer to the official documentation: let store_directory = config.ensure_store_directory(); let tools: Vec = config.tools.keys().cloned().collect(); - let sync_progress = SyncProgress::new(tools); + let tags: Vec = config + .tools + .values() + .map(|config_asset| config_asset.tag.clone().unwrap_or_else(|| "latest".into())) + .collect(); + let sync_progress = SyncProgress::new(tools, tags); let installer = Installer::mk(store_directory, sync_progress); for (tool_name, config_asset) in config.tools.iter() { diff --git a/src/sync/download.rs b/src/sync/download.rs index e60722d..2307b17 100644 --- a/src/sync/download.rs +++ b/src/sync/download.rs @@ -137,7 +137,7 @@ mod tests { asset_name: "ASSET_NAME", version: &ToolInfoTag::Latest.to_str_version(), pb_msg: &ProgressBar::hidden(), - sync_progress: &SyncProgress::new(vec!["tool".to_string()]), + sync_progress: &SyncProgress::new(vec!["tool".to_string()], vec!["latest".to_string()]), }; assert_eq!( @@ -154,7 +154,7 @@ mod tests { asset_name: "ASSET_NAME", version: &ToolInfoTag::Specific("SPECIFIC_TAG".to_string()).to_str_version(), pb_msg: &ProgressBar::hidden(), - sync_progress: &SyncProgress::new(vec!["tool".to_string()]), + sync_progress: &SyncProgress::new(vec!["tool".to_string()], vec!["latest".to_string()]), }; assert_eq!( diff --git a/src/sync/install.rs b/src/sync/install.rs index fb69e34..7714441 100644 --- a/src/sync/install.rs +++ b/src/sync/install.rs @@ -41,7 +41,8 @@ impl Installer { } pub fn install(&self, tool_name: &str, config_asset: &ConfigAsset) { - let pb_msg = self.sync_progress.create_message_bar(tool_name); + let tag: String = config_asset.tag.clone().unwrap_or_else(|| "latest".into()); + let pb_msg = self.sync_progress.create_message_bar(tool_name, &tag); match configure_tool(tool_name, config_asset) { Tool::Known(tool_info) => match self.sync_single_tool(&tool_info, &pb_msg) { @@ -50,11 +51,12 @@ impl Installer { } Err(e) => { self.sync_progress - .failure(pb_msg, tool_name, format!("[error] {}", e)); + .failure(pb_msg, tool_name, &tag, format!("[error] {}", e)); } }, Tool::Error(e) => { - self.sync_progress.failure(pb_msg, tool_name, e.display()); + self.sync_progress + .failure(pb_msg, tool_name, &tag, e.display()); } } } diff --git a/src/sync/progress.rs b/src/sync/progress.rs index bbf30f3..be28e8f 100644 --- a/src/sync/progress.rs +++ b/src/sync/progress.rs @@ -3,46 +3,57 @@ use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; pub struct SyncProgress { max_tool_size: usize, + max_tag_size: usize, multi_progress: MultiProgress, } const SUCCESS: Emoji<'_, '_> = Emoji("✅ ", "OK "); const FAILURE: Emoji<'_, '_> = Emoji("⛔ ", "NO "); const PROCESS: Emoji<'_, '_> = Emoji("📥 ", ".. "); +const MIN_TAG_SIZE: usize = 8; impl SyncProgress { /// Creates new `SyncProgress` from a list of tools. /// !!! The given `Vec` must be non-empty !!! - pub fn new(tools: Vec) -> SyncProgress { + pub fn new(tools: Vec, tags: Vec) -> SyncProgress { // unwrap is safe here because 'new' is called with a non-empty vector let max_tool_size = tools.iter().map(|tool| tool.len()).max().unwrap(); + // putting a default of 8 here since tags like v0.10.10 is already 8 + let max_tag_size = tags + .iter() + .map(|tag| std::cmp::max(tag.len(), MIN_TAG_SIZE)) + .max() + .unwrap_or(MIN_TAG_SIZE); + let multi_progress = MultiProgress::new(); SyncProgress { max_tool_size, + max_tag_size, multi_progress, } } fn fmt_prefix(&self, emoji: Emoji, tool_name: &str, tag_name: &str) -> String { let aligned_tool = format!( - "{:width$} {:<8}", + "{:tool_width$} {:tag_width$}", tool_name, tag_name, - width = self.max_tool_size + tool_width = self.max_tool_size, + tag_width = self.max_tag_size, ); format!("{}{}", emoji, aligned_tool) } - pub fn create_message_bar(&self, tool_name: &str) -> ProgressBar { + pub fn create_message_bar(&self, tool_name: &str, tag_name: &str) -> ProgressBar { let message_style = ProgressStyle::with_template("{prefix:.bold.dim} {msg}").unwrap(); self.multi_progress.add( ProgressBar::new(100) .with_style(message_style) - .with_prefix(self.fmt_prefix(PROCESS, tool_name, "")), + .with_prefix(self.fmt_prefix(PROCESS, tool_name, tag_name)), ) } @@ -66,11 +77,58 @@ impl SyncProgress { pb.finish(); } - pub fn failure(&self, pb: ProgressBar, tool_name: &str, err_msg: String) { - pb.set_prefix(self.fmt_prefix(FAILURE, tool_name, "")); + pub fn failure(&self, pb: ProgressBar, tool_name: &str, tag_name: &str, err_msg: String) { + pb.set_prefix(self.fmt_prefix(FAILURE, tool_name, tag_name)); let failure_msg = format!("{}", style(err_msg).red()); pb.set_message(failure_msg); pb.finish(); } } + +#[cfg(test)] +mod tests { + use super::SyncProgress; + + #[test] + fn test_max_tag_size_specific() { + let tags: Vec = vec![ + String::from("v10.10.100"), + String::from("latest"), + String::from("latest"), + ]; + let tools: Vec = vec![ + String::from("ripgrep"), + String::from("bat"), + String::from("exa"), + ]; + + let progres = SyncProgress::new(tools, tags); + + // v10.10.100 is 10 characters + assert_eq!(progres.max_tag_size, 10); + // ripgrep is 7 characters + assert_eq!(progres.max_tool_size, 7); + } + + #[test] + fn test_max_tag_size_latest() { + let tags: Vec = vec![ + String::from("latest"), + String::from("latest"), + String::from("latest"), + ]; + let tools: Vec = vec![ + String::from("ripgrep"), + String::from("bat"), + String::from("exa"), + ]; + + let progres = SyncProgress::new(tools, tags); + + // latest is 6 characters so it should default to 8 + assert_eq!(progres.max_tag_size, 8); + // ripgrep is 7 characters + assert_eq!(progres.max_tool_size, 7); + } +}