refactor(cli): consolidate output through output module
- 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
This commit is contained in:
parent
c32a6fbe8b
commit
b863e025d5
|
|
@ -35,7 +35,7 @@ pub fn execute(path: String, name: String) -> Result<()> {
|
||||||
config.save_to_file(&config_path)
|
config.save_to_file(&config_path)
|
||||||
.context("Failed to save config")?;
|
.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("Created default list \"My Tasks\"");
|
||||||
output::success(&format!("Set \"{}\" as current workspace", name));
|
output::success(&format!("Set \"{}\" as current workspace", name));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,21 @@
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use colored::*;
|
use colored::*;
|
||||||
|
use bevy_tasks_core::{Task, TaskStatus};
|
||||||
use crate::output;
|
use crate::output;
|
||||||
use crate::commands::get_repository;
|
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<String>) -> Result<()> {
|
pub fn create(name: String, workspace: Option<String>) -> Result<()> {
|
||||||
let (mut repo, _workspace_name) = get_repository(workspace)?;
|
let (mut repo, _workspace_name) = get_repository(workspace)?;
|
||||||
|
|
||||||
|
|
@ -21,7 +34,7 @@ pub fn show(list_name: Option<String>, workspace: Option<String>) -> Result<()>
|
||||||
.context("Failed to get lists")?;
|
.context("Failed to get lists")?;
|
||||||
|
|
||||||
if lists.is_empty() {
|
if lists.is_empty() {
|
||||||
println!("No lists found. Create one with 'bevy-tasks list create <name>'");
|
output::info("No lists found. Create one with 'bevy-tasks list create <name>'");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -31,54 +44,14 @@ pub fn show(list_name: Option<String>, workspace: Option<String>) -> Result<()>
|
||||||
.find(|l| l.title == name)
|
.find(|l| l.title == name)
|
||||||
.ok_or_else(|| anyhow::anyhow!("List '{}' not found", name))?;
|
.ok_or_else(|| anyhow::anyhow!("List '{}' not found", name))?;
|
||||||
|
|
||||||
println!("{} {} {}", list.title.bold(), format!("({} tasks)", list.tasks.len()).dimmed(), "");
|
output::header(&format!("{} ({})", list.title, format!("{} tasks", list.tasks.len()).dimmed()));
|
||||||
|
print_tasks(&list.tasks);
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Show all lists
|
// Show all lists
|
||||||
for list in &lists {
|
for list in &lists {
|
||||||
println!("{} {}", list.title.bold(), format!("({} tasks)", list.tasks.len()).dimmed());
|
output::header(&format!("{} ({})", list.title, format!("{} tasks", list.tasks.len()).dimmed()));
|
||||||
|
print_tasks(&list.tasks);
|
||||||
if list.tasks.is_empty() {
|
output::blank();
|
||||||
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!();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,7 +78,7 @@ pub fn delete(name: String, workspace: Option<String>) -> Result<()> {
|
||||||
io::stdin().read_line(&mut input)?;
|
io::stdin().read_line(&mut input)?;
|
||||||
|
|
||||||
if input.trim().to_lowercase() != "y" {
|
if input.trim().to_lowercase() != "y" {
|
||||||
println!("Cancelled");
|
output::info("Cancelled");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,8 @@ pub fn setup(workspace_name: Option<String>) -> Result<()> {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Prompt for WebDAV URL
|
// Prompt for WebDAV URL
|
||||||
println!("WebDAV sync setup for workspace \"{}\"", name.green());
|
output::header(&format!("WebDAV sync setup for workspace \"{}\"", name.green()));
|
||||||
println!();
|
output::blank();
|
||||||
|
|
||||||
let url = prompt("WebDAV URL: ")?;
|
let url = prompt("WebDAV URL: ")?;
|
||||||
if url.is_empty() {
|
if url.is_empty() {
|
||||||
|
|
@ -35,8 +35,8 @@ pub fn setup(workspace_name: Option<String>) -> Result<()> {
|
||||||
.context("Failed to read password")?;
|
.context("Failed to read password")?;
|
||||||
|
|
||||||
// Test connection
|
// Test connection
|
||||||
println!();
|
output::blank();
|
||||||
println!("Testing connection...");
|
output::info("Testing connection...");
|
||||||
|
|
||||||
let rt = tokio::runtime::Runtime::new().context("Failed to create async runtime")?;
|
let rt = tokio::runtime::Runtime::new().context("Failed to create async runtime")?;
|
||||||
let client = WebDavClient::new(&url, &username, &password);
|
let client = WebDavClient::new(&url, &username, &password);
|
||||||
|
|
@ -102,7 +102,7 @@ pub fn execute(mode: SyncMode, workspace_name: Option<String>) -> Result<()> {
|
||||||
SyncMode::Push => "Pushing",
|
SyncMode::Push => "Pushing",
|
||||||
SyncMode::Pull => "Pulling",
|
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 rt = tokio::runtime::Runtime::new().context("Failed to create async runtime")?;
|
||||||
let result = rt.block_on(sync_workspace(
|
let result = rt.block_on(sync_workspace(
|
||||||
|
|
@ -153,7 +153,7 @@ pub fn status(workspace_name: Option<String>, all: bool) -> Result<()> {
|
||||||
if ws.webdav_url.is_some() {
|
if ws.webdav_url.is_some() {
|
||||||
found_any = true;
|
found_any = true;
|
||||||
print_workspace_status(&name, &ws.path, ws.webdav_url.as_deref())?;
|
print_workspace_status(&name, &ws.path, ws.webdav_url.as_deref())?;
|
||||||
println!();
|
output::blank();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !found_any {
|
if !found_any {
|
||||||
|
|
@ -178,27 +178,27 @@ pub fn status(workspace_name: Option<String>, all: bool) -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_workspace_status(name: &str, path: &std::path::Path, webdav_url: Option<&str>) -> 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 {
|
if let Some(url) = webdav_url {
|
||||||
println!(" WebDAV URL: {}", url);
|
output::detail("WebDAV URL", url);
|
||||||
} else {
|
} else {
|
||||||
println!(" WebDAV: {}", "not configured".dimmed());
|
output::detail("WebDAV", &"not configured".dimmed().to_string());
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let info = get_sync_status(path)?;
|
let info = get_sync_status(path)?;
|
||||||
|
|
||||||
if let Some(last) = info.last_sync {
|
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 {
|
} else {
|
||||||
println!(" Last sync: {}", "never".dimmed());
|
output::detail("Last sync", &"never".dimmed().to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
println!(" Tracked files: {}", info.tracked_files);
|
output::detail("Tracked files", &info.tracked_files.to_string());
|
||||||
println!(" Pending changes: {}", info.pending_changes);
|
output::detail("Pending changes", &info.pending_changes.to_string());
|
||||||
if info.queued_operations > 0 {
|
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(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,7 @@ pub fn delete(task_id_str: String, workspace: Option<String>) -> Result<()> {
|
||||||
let mut input = String::new();
|
let mut input = String::new();
|
||||||
io::stdin().read_line(&mut input)?;
|
io::stdin().read_line(&mut input)?;
|
||||||
if input.trim().to_lowercase() != "y" {
|
if input.trim().to_lowercase() != "y" {
|
||||||
println!("Cancelled");
|
output::info("Cancelled");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ pub fn add(name: String, path: String) -> Result<()> {
|
||||||
// Save config
|
// Save config
|
||||||
save_config(&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\"");
|
output::success("Created default list \"My Tasks\"");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -48,7 +48,7 @@ pub fn list() -> Result<()> {
|
||||||
let config = load_config()?;
|
let config = load_config()?;
|
||||||
|
|
||||||
if config.workspaces.is_empty() {
|
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(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -63,7 +63,7 @@ pub fn list() -> Result<()> {
|
||||||
} else {
|
} else {
|
||||||
"".normal()
|
"".normal()
|
||||||
};
|
};
|
||||||
println!(" {}: {:?}{}", name, workspace_config.path, marker);
|
output::item(&format!("{}: {}{}", name, workspace_config.path.display(), marker));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -103,7 +103,7 @@ pub fn remove(name: String) -> Result<()> {
|
||||||
io::stdin().read_line(&mut input)?;
|
io::stdin().read_line(&mut input)?;
|
||||||
|
|
||||||
if input.trim().to_lowercase() != "y" {
|
if input.trim().to_lowercase() != "y" {
|
||||||
println!("Cancelled");
|
output::info("Cancelled");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -134,7 +134,7 @@ pub fn retarget(name: String, path: String) -> Result<()> {
|
||||||
config.add_workspace(name.clone(), WorkspaceConfig::new(path_buf.clone()));
|
config.add_workspace(name.clone(), WorkspaceConfig::new(path_buf.clone()));
|
||||||
save_config(&config)?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -155,7 +155,7 @@ pub fn migrate(name: String, new_path: String) -> Result<()> {
|
||||||
.path.clone();
|
.path.clone();
|
||||||
|
|
||||||
// Confirm
|
// 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): ");
|
print!("Continue? (y/n): ");
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
io::stdout().flush()?;
|
io::stdout().flush()?;
|
||||||
|
|
@ -164,7 +164,7 @@ pub fn migrate(name: String, new_path: String) -> Result<()> {
|
||||||
io::stdin().read_line(&mut input)?;
|
io::stdin().read_line(&mut input)?;
|
||||||
|
|
||||||
if input.trim().to_lowercase() != "y" {
|
if input.trim().to_lowercase() != "y" {
|
||||||
println!("Cancelled");
|
output::info("Cancelled");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -172,7 +172,7 @@ pub fn migrate(name: String, new_path: String) -> Result<()> {
|
||||||
std::fs::create_dir_all(&new_path_buf)?;
|
std::fs::create_dir_all(&new_path_buf)?;
|
||||||
|
|
||||||
// Move files
|
// Move files
|
||||||
println!("Moving files...");
|
output::info("Moving files...");
|
||||||
let entries = std::fs::read_dir(&old_path)?;
|
let entries = std::fs::read_dir(&old_path)?;
|
||||||
let mut count = 0;
|
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();
|
let mut options = fs_extra::dir::CopyOptions::new();
|
||||||
options.copy_inside = true;
|
options.copy_inside = true;
|
||||||
fs_extra::dir::move_dir(entry.path(), &new_path_buf, &options)?;
|
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 {
|
} else {
|
||||||
std::fs::rename(entry.path(), dest)?;
|
std::fs::rename(entry.path(), dest)?;
|
||||||
println!(" Moved {:?}", file_name);
|
output::item(&format!("Moved {}", file_name.to_string_lossy()));
|
||||||
}
|
}
|
||||||
count += 1;
|
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()));
|
config.add_workspace(name.clone(), WorkspaceConfig::new(new_path_buf.clone()));
|
||||||
save_config(&config)?;
|
save_config(&config)?;
|
||||||
|
|
||||||
output::success(&format!("Migrated {} items to {:?}", count, 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));
|
output::success(&format!("Workspace \"{}\" now points to {}", name, new_path_buf.display()));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,3 +15,19 @@ pub fn warning(message: &str) {
|
||||||
pub fn info(message: &str) {
|
pub fn info(message: &str) {
|
||||||
println!("{} {}", "ℹ".blue(), message);
|
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!();
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue