From b863e025d59435ca2fb902de0ae8d1e3d5ace937 Mon Sep 17 00:00:00 2001 From: Tristan Michael Date: Mon, 30 Mar 2026 16:29:57 -0700 Subject: [PATCH] refactor(cli): consolidate output through output module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add header(), detail(), item(), blank() functions to output.rs - Replace all raw println!() calls with output::* equivalents - Fix {:?} debug format for paths — use .display() for clean output - Extract print_tasks() helper to deduplicate task display logic - Consistent formatting: info for empty states, item for list entries --- crates/bevy-tasks-cli/src/commands/init.rs | 2 +- crates/bevy-tasks-cli/src/commands/list.rs | 67 ++++++------------- crates/bevy-tasks-cli/src/commands/sync.rs | 28 ++++---- crates/bevy-tasks-cli/src/commands/task.rs | 2 +- .../bevy-tasks-cli/src/commands/workspace.rs | 24 +++---- crates/bevy-tasks-cli/src/output.rs | 16 +++++ 6 files changed, 64 insertions(+), 75 deletions(-) diff --git a/crates/bevy-tasks-cli/src/commands/init.rs b/crates/bevy-tasks-cli/src/commands/init.rs index 067c1c8..743b7f7 100644 --- a/crates/bevy-tasks-cli/src/commands/init.rs +++ b/crates/bevy-tasks-cli/src/commands/init.rs @@ -35,7 +35,7 @@ pub fn execute(path: String, name: String) -> Result<()> { config.save_to_file(&config_path) .context("Failed to save config")?; - output::success(&format!("Initialized workspace \"{}\" at {:?}", name, path_buf)); + output::success(&format!("Initialized workspace \"{}\" at {}", name, path_buf.display())); output::success("Created default list \"My Tasks\""); output::success(&format!("Set \"{}\" as current workspace", name)); diff --git a/crates/bevy-tasks-cli/src/commands/list.rs b/crates/bevy-tasks-cli/src/commands/list.rs index fb971ee..56fa32b 100644 --- a/crates/bevy-tasks-cli/src/commands/list.rs +++ b/crates/bevy-tasks-cli/src/commands/list.rs @@ -1,8 +1,21 @@ use anyhow::{Context, Result}; use colored::*; +use bevy_tasks_core::{Task, TaskStatus}; use crate::output; use crate::commands::get_repository; +fn print_tasks(tasks: &[Task]) { + if tasks.is_empty() { + output::item("No tasks"); + return; + } + for task in tasks { + let checkbox = if task.status == TaskStatus::Completed { "[✓]".green() } else { "[ ]".normal() }; + let due_str = task.due_date.map(|d| format!(" (due: {})", d.format("%Y-%m-%d")).yellow().to_string()).unwrap_or_default(); + output::item(&format!("{} {}{} {}", checkbox, task.title, due_str, task.id.to_string().dimmed())); + } +} + pub fn create(name: String, workspace: Option) -> Result<()> { let (mut repo, _workspace_name) = get_repository(workspace)?; @@ -21,7 +34,7 @@ pub fn show(list_name: Option, workspace: Option) -> Result<()> .context("Failed to get lists")?; if lists.is_empty() { - println!("No lists found. Create one with 'bevy-tasks list create '"); + output::info("No lists found. Create one with 'bevy-tasks list create '"); return Ok(()); } @@ -31,54 +44,14 @@ pub fn show(list_name: Option, workspace: Option) -> Result<()> .find(|l| l.title == name) .ok_or_else(|| anyhow::anyhow!("List '{}' not found", name))?; - println!("{} {} {}", list.title.bold(), format!("({} tasks)", list.tasks.len()).dimmed(), ""); - - if list.tasks.is_empty() { - println!(" No tasks"); - } else { - for task in &list.tasks { - let checkbox = if task.status == bevy_tasks_core::TaskStatus::Completed { - "[✓]".green() - } else { - "[ ]".normal() - }; - - let due_str = if let Some(due) = task.due_date { - format!(" (due: {})", due.format("%Y-%m-%d")).yellow().to_string() - } else { - String::new() - }; - - let id_str = task.id.to_string(); - println!(" {} {}{} {}", checkbox, task.title, due_str, id_str.dimmed()); - } - } + output::header(&format!("{} ({})", list.title, format!("{} tasks", list.tasks.len()).dimmed())); + print_tasks(&list.tasks); } else { // Show all lists for list in &lists { - println!("{} {}", list.title.bold(), format!("({} tasks)", list.tasks.len()).dimmed()); - - if list.tasks.is_empty() { - println!(" No tasks"); - } else { - for task in &list.tasks { - let checkbox = if task.status == bevy_tasks_core::TaskStatus::Completed { - "[✓]".green() - } else { - "[ ]".normal() - }; - - let due_str = if let Some(due) = task.due_date { - format!(" (due: {})", due.format("%Y-%m-%d")).yellow().to_string() - } else { - String::new() - }; - - let id_str = task.id.to_string(); - println!(" {} {}{} {}", checkbox, task.title, due_str, id_str.dimmed()); - } - } - println!(); + output::header(&format!("{} ({})", list.title, format!("{} tasks", list.tasks.len()).dimmed())); + print_tasks(&list.tasks); + output::blank(); } } @@ -105,7 +78,7 @@ pub fn delete(name: String, workspace: Option) -> Result<()> { io::stdin().read_line(&mut input)?; if input.trim().to_lowercase() != "y" { - println!("Cancelled"); + output::info("Cancelled"); return Ok(()); } diff --git a/crates/bevy-tasks-cli/src/commands/sync.rs b/crates/bevy-tasks-cli/src/commands/sync.rs index 4bf0ea3..6e00b3d 100644 --- a/crates/bevy-tasks-cli/src/commands/sync.rs +++ b/crates/bevy-tasks-cli/src/commands/sync.rs @@ -21,8 +21,8 @@ pub fn setup(workspace_name: Option) -> Result<()> { }; // Prompt for WebDAV URL - println!("WebDAV sync setup for workspace \"{}\"", name.green()); - println!(); + output::header(&format!("WebDAV sync setup for workspace \"{}\"", name.green())); + output::blank(); let url = prompt("WebDAV URL: ")?; if url.is_empty() { @@ -35,8 +35,8 @@ pub fn setup(workspace_name: Option) -> Result<()> { .context("Failed to read password")?; // Test connection - println!(); - println!("Testing connection..."); + output::blank(); + output::info("Testing connection..."); let rt = tokio::runtime::Runtime::new().context("Failed to create async runtime")?; let client = WebDavClient::new(&url, &username, &password); @@ -102,7 +102,7 @@ pub fn execute(mode: SyncMode, workspace_name: Option) -> Result<()> { SyncMode::Push => "Pushing", SyncMode::Pull => "Pulling", }; - println!("{} workspace \"{}\"...", mode_str, name.green()); + output::info(&format!("{} workspace \"{}\"...", mode_str, name.green())); let rt = tokio::runtime::Runtime::new().context("Failed to create async runtime")?; let result = rt.block_on(sync_workspace( @@ -153,7 +153,7 @@ pub fn status(workspace_name: Option, all: bool) -> Result<()> { if ws.webdav_url.is_some() { found_any = true; print_workspace_status(&name, &ws.path, ws.webdav_url.as_deref())?; - println!(); + output::blank(); } } if !found_any { @@ -178,27 +178,27 @@ pub fn status(workspace_name: Option, all: bool) -> Result<()> { } fn print_workspace_status(name: &str, path: &std::path::Path, webdav_url: Option<&str>) -> Result<()> { - println!("Workspace: {}", name.green()); + output::header(&format!("Workspace: {}", name.green())); if let Some(url) = webdav_url { - println!(" WebDAV URL: {}", url); + output::detail("WebDAV URL", url); } else { - println!(" WebDAV: {}", "not configured".dimmed()); + output::detail("WebDAV", &"not configured".dimmed().to_string()); return Ok(()); } let info = get_sync_status(path)?; if let Some(last) = info.last_sync { - println!(" Last sync: {}", last.format("%Y-%m-%d %H:%M:%S UTC")); + output::detail("Last sync", &last.format("%Y-%m-%d %H:%M:%S UTC").to_string()); } else { - println!(" Last sync: {}", "never".dimmed()); + output::detail("Last sync", &"never".dimmed().to_string()); } - println!(" Tracked files: {}", info.tracked_files); - println!(" Pending changes: {}", info.pending_changes); + output::detail("Tracked files", &info.tracked_files.to_string()); + output::detail("Pending changes", &info.pending_changes.to_string()); if info.queued_operations > 0 { - println!(" Queued operations: {}", format!("{}", info.queued_operations).yellow()); + output::detail("Queued operations", &format!("{}", info.queued_operations).yellow().to_string()); } Ok(()) diff --git a/crates/bevy-tasks-cli/src/commands/task.rs b/crates/bevy-tasks-cli/src/commands/task.rs index 32f7cd5..33a4593 100644 --- a/crates/bevy-tasks-cli/src/commands/task.rs +++ b/crates/bevy-tasks-cli/src/commands/task.rs @@ -100,7 +100,7 @@ pub fn delete(task_id_str: String, workspace: Option) -> Result<()> { let mut input = String::new(); io::stdin().read_line(&mut input)?; if input.trim().to_lowercase() != "y" { - println!("Cancelled"); + output::info("Cancelled"); return Ok(()); } diff --git a/crates/bevy-tasks-cli/src/commands/workspace.rs b/crates/bevy-tasks-cli/src/commands/workspace.rs index 381719e..b616472 100644 --- a/crates/bevy-tasks-cli/src/commands/workspace.rs +++ b/crates/bevy-tasks-cli/src/commands/workspace.rs @@ -38,7 +38,7 @@ pub fn add(name: String, path: String) -> Result<()> { // Save config save_config(&config)?; - output::success(&format!("Added workspace \"{}\" at {:?}", name, path_buf)); + output::success(&format!("Added workspace \"{}\" at {}", name, path_buf.display())); output::success("Created default list \"My Tasks\""); Ok(()) @@ -48,7 +48,7 @@ pub fn list() -> Result<()> { let config = load_config()?; if config.workspaces.is_empty() { - println!("No workspaces configured. Use 'bevy-tasks init' to create one."); + output::info("No workspaces configured. Use 'bevy-tasks init' to create one."); return Ok(()); } @@ -63,7 +63,7 @@ pub fn list() -> Result<()> { } else { "".normal() }; - println!(" {}: {:?}{}", name, workspace_config.path, marker); + output::item(&format!("{}: {}{}", name, workspace_config.path.display(), marker)); } Ok(()) @@ -103,7 +103,7 @@ pub fn remove(name: String) -> Result<()> { io::stdin().read_line(&mut input)?; if input.trim().to_lowercase() != "y" { - println!("Cancelled"); + output::info("Cancelled"); return Ok(()); } @@ -134,7 +134,7 @@ pub fn retarget(name: String, path: String) -> Result<()> { config.add_workspace(name.clone(), WorkspaceConfig::new(path_buf.clone())); save_config(&config)?; - output::success(&format!("Workspace \"{}\" now points to {:?}", name, path_buf)); + output::success(&format!("Workspace \"{}\" now points to {}", name, path_buf.display())); Ok(()) } @@ -155,7 +155,7 @@ pub fn migrate(name: String, new_path: String) -> Result<()> { .path.clone(); // Confirm - output::warning(&format!("This will move all files from {:?} to {:?}", old_path, new_path_buf)); + output::warning(&format!("This will move all files from {} to {}", old_path.display(), new_path_buf.display())); print!("Continue? (y/n): "); use std::io::{self, Write}; io::stdout().flush()?; @@ -164,7 +164,7 @@ pub fn migrate(name: String, new_path: String) -> Result<()> { io::stdin().read_line(&mut input)?; if input.trim().to_lowercase() != "y" { - println!("Cancelled"); + output::info("Cancelled"); return Ok(()); } @@ -172,7 +172,7 @@ pub fn migrate(name: String, new_path: String) -> Result<()> { std::fs::create_dir_all(&new_path_buf)?; // Move files - println!("Moving files..."); + output::info("Moving files..."); let entries = std::fs::read_dir(&old_path)?; let mut count = 0; @@ -185,10 +185,10 @@ pub fn migrate(name: String, new_path: String) -> Result<()> { let mut options = fs_extra::dir::CopyOptions::new(); options.copy_inside = true; fs_extra::dir::move_dir(entry.path(), &new_path_buf, &options)?; - println!(" Moved {:?}/", file_name); + output::item(&format!("Moved {}/", file_name.to_string_lossy())); } else { std::fs::rename(entry.path(), dest)?; - println!(" Moved {:?}", file_name); + output::item(&format!("Moved {}", file_name.to_string_lossy())); } count += 1; } @@ -202,8 +202,8 @@ pub fn migrate(name: String, new_path: String) -> Result<()> { config.add_workspace(name.clone(), WorkspaceConfig::new(new_path_buf.clone())); save_config(&config)?; - output::success(&format!("Migrated {} items to {:?}", count, new_path_buf)); - output::success(&format!("Workspace \"{}\" now points to {:?}", name, new_path_buf)); + output::success(&format!("Migrated {} items to {}", count, new_path_buf.display())); + output::success(&format!("Workspace \"{}\" now points to {}", name, new_path_buf.display())); Ok(()) } diff --git a/crates/bevy-tasks-cli/src/output.rs b/crates/bevy-tasks-cli/src/output.rs index cb535ba..dd49031 100644 --- a/crates/bevy-tasks-cli/src/output.rs +++ b/crates/bevy-tasks-cli/src/output.rs @@ -15,3 +15,19 @@ pub fn warning(message: &str) { pub fn info(message: &str) { println!("{} {}", "ℹ".blue(), message); } + +pub fn header(message: &str) { + println!("{}", message.bold()); +} + +pub fn detail(label: &str, value: &str) { + println!(" {}: {}", label, value); +} + +pub fn item(message: &str) { + println!(" {}", message); +} + +pub fn blank() { + println!(); +}