Skip to main content

blvm_sdk/cli/
output.rs

1//! # CLI Output Formatting
2//!
3//! Output formatting utilities for CLI tools.
4
5use serde::Serialize;
6use std::fmt;
7
8/// Output format options
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub enum OutputFormat {
11    /// Human-readable text output
12    Text,
13    /// JSON output
14    Json,
15}
16
17impl std::str::FromStr for OutputFormat {
18    type Err = String;
19
20    fn from_str(s: &str) -> Result<Self, Self::Err> {
21        match s.to_lowercase().as_str() {
22            "text" | "txt" => Ok(OutputFormat::Text),
23            "json" => Ok(OutputFormat::Json),
24            _ => Err(format!("Invalid output format: {s}")),
25        }
26    }
27}
28
29/// Output formatter for CLI tools
30pub struct OutputFormatter {
31    format: OutputFormat,
32}
33
34impl OutputFormatter {
35    /// Create a new output formatter
36    pub fn new(format: OutputFormat) -> Self {
37        Self { format }
38    }
39
40    /// Format a value for output
41    pub fn format<T>(&self, value: &T) -> Result<String, String>
42    where
43        T: Serialize + fmt::Display,
44    {
45        match self.format {
46            OutputFormat::Text => Ok(value.to_string()),
47            OutputFormat::Json => serde_json::to_string_pretty(value)
48                .map_err(|e| format!("JSON serialization error: {e}")),
49        }
50    }
51
52    /// Format an error for output
53    pub fn format_error(&self, error: &dyn std::error::Error) -> String {
54        match self.format {
55            OutputFormat::Text => format!("Error: {error}"),
56            OutputFormat::Json => {
57                let error_json = serde_json::json!({
58                    "error": true,
59                    "message": error.to_string()
60                });
61                serde_json::to_string_pretty(&error_json)
62                    .unwrap_or_else(|_| format!("{{\"error\": true, \"message\": \"{error}\"}}"))
63            }
64        }
65    }
66
67    /// Format a success message
68    pub fn format_success(&self, message: &str) -> String {
69        match self.format {
70            OutputFormat::Text => format!("Success: {message}"),
71            OutputFormat::Json => {
72                let success_json = serde_json::json!({
73                    "success": true,
74                    "message": message
75                });
76                serde_json::to_string_pretty(&success_json).unwrap_or_else(|_| {
77                    format!("{{\"success\": true, \"message\": \"{message}\"}}")
78                })
79            }
80        }
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87
88    #[test]
89    fn test_output_format_parsing() {
90        assert_eq!("text".parse::<OutputFormat>().unwrap(), OutputFormat::Text);
91        assert_eq!("txt".parse::<OutputFormat>().unwrap(), OutputFormat::Text);
92        assert_eq!("json".parse::<OutputFormat>().unwrap(), OutputFormat::Json);
93        assert!("invalid".parse::<OutputFormat>().is_err());
94    }
95
96    #[test]
97    fn test_text_formatting() {
98        let formatter = OutputFormatter::new(OutputFormat::Text);
99        let result = formatter.format(&"test message");
100        assert_eq!(result.unwrap(), "test message");
101    }
102
103    #[test]
104    fn test_json_formatting() {
105        let formatter = OutputFormatter::new(OutputFormat::Json);
106        let result = formatter.format(&serde_json::json!({"message": "test"}));
107        assert!(result.unwrap().contains("test"));
108    }
109}