1use colored::{ColoredString, Colorize};
4use std::fmt::Display;
5
6pub struct OutputFormatter;
8
9impl OutputFormatter {
10 pub fn success(message: impl Display) -> ColoredString {
12 format!("✓ {message}").green()
13 }
14
15 pub fn error(message: impl Display) -> ColoredString {
17 format!("✗ {message}").red()
18 }
19
20 pub fn warning(message: impl Display) -> ColoredString {
22 format!("⚠ {message}").yellow()
23 }
24
25 pub fn info(message: impl Display) -> ColoredString {
27 format!("ℹ {message}").blue()
28 }
29
30 pub fn debug(message: impl Display) -> ColoredString {
32 format!("🔍 {message}").dimmed()
33 }
34
35 pub fn label(label: impl Display) -> ColoredString {
37 format!("{label}:").bold()
38 }
39
40 pub fn command(cmd: impl Display) -> ColoredString {
42 format!("`{cmd}`").cyan()
43 }
44
45 pub fn path(path: impl Display) -> ColoredString {
47 path.to_string().yellow()
48 }
49
50 pub fn key(key: impl Display) -> ColoredString {
52 key.to_string().magenta()
53 }
54
55 pub fn dim(message: impl Display) -> ColoredString {
57 message.to_string().dimmed()
58 }
59
60 pub fn progress(current: usize, total: usize, message: impl Display) -> String {
62 format!("[{current}/{total}] {message}")
63 }
64
65 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
73pub trait Contextual {
75 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 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}