raz_common/
output.rs

1//! Output formatting utilities for consistent CLI output
2
3use colored::{ColoredString, Colorize};
4use std::fmt::Display;
5
6/// Output formatter for consistent styling across the project
7pub struct OutputFormatter;
8
9impl OutputFormatter {
10    /// Format a success message
11    pub fn success(message: impl Display) -> ColoredString {
12        format!("✓ {message}").green()
13    }
14
15    /// Format an error message
16    pub fn error(message: impl Display) -> ColoredString {
17        format!("✗ {message}").red()
18    }
19
20    /// Format a warning message
21    pub fn warning(message: impl Display) -> ColoredString {
22        format!("⚠ {message}").yellow()
23    }
24
25    /// Format an info message
26    pub fn info(message: impl Display) -> ColoredString {
27        format!("ℹ {message}").blue()
28    }
29
30    /// Format a debug message
31    pub fn debug(message: impl Display) -> ColoredString {
32        format!("🔍 {message}").dimmed()
33    }
34
35    /// Format a label (like "Info:", "Error:", etc.)
36    pub fn label(label: impl Display) -> ColoredString {
37        format!("{label}:").bold()
38    }
39
40    /// Format a command for display
41    pub fn command(cmd: impl Display) -> ColoredString {
42        format!("`{cmd}`").cyan()
43    }
44
45    /// Format a file path
46    pub fn path(path: impl Display) -> ColoredString {
47        path.to_string().yellow()
48    }
49
50    /// Format a key or identifier
51    pub fn key(key: impl Display) -> ColoredString {
52        key.to_string().magenta()
53    }
54
55    /// Format a dimmed/secondary message
56    pub fn dim(message: impl Display) -> ColoredString {
57        message.to_string().dimmed()
58    }
59
60    /// Create a progress indicator
61    pub fn progress(current: usize, total: usize, message: impl Display) -> String {
62        format!("[{current}/{total}] {message}")
63    }
64
65    /// Create a header with consistent formatting
66    pub fn header(title: impl Display) -> String {
67        let title_str = title.to_string();
68        let line = "─".repeat(title_str.len() + 4);
69        format!("{}\n  {}  \n{}", line, title_str.bold(), line)
70    }
71}
72
73/// Trait for types that can be formatted with context
74pub trait Contextual {
75    /// Format with additional context
76    fn with_context(&self, context: impl Display) -> String;
77}
78
79impl<T: Display> Contextual for T {
80    fn with_context(&self, context: impl Display) -> String {
81        format!("{context}: {self}")
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88
89    #[test]
90    fn test_output_formatter() {
91        // Just test that methods don't panic
92        let _ = OutputFormatter::success("Test");
93        let _ = OutputFormatter::error("Test");
94        let _ = OutputFormatter::warning("Test");
95        let _ = OutputFormatter::info("Test");
96        let _ = OutputFormatter::command("cargo test");
97        let _ = OutputFormatter::path("/some/path");
98        let _ = OutputFormatter::progress(1, 10, "Processing");
99    }
100
101    #[test]
102    fn test_contextual() {
103        let msg = "error occurred";
104        let with_ctx = msg.with_context("File processing");
105        assert_eq!(with_ctx, "File processing: error occurred");
106    }
107}