use serde::Serialize;
use std::io::{self};
pub struct OutputFormatter {
json: bool,
}
impl OutputFormatter {
pub fn new(json: bool) -> Self {
Self { json }
}
pub fn is_json(&self) -> bool {
self.json
}
pub fn print<T: Serialize>(&self, value: &T) -> io::Result<()> {
if self.json {
self.print_json(value)
} else {
self.print_json(value)
}
}
fn print_json<T: Serialize>(&self, value: &T) -> io::Result<()> {
let json = serde_json::to_string_pretty(value).map_err(io::Error::other)?;
println!("{}", json);
Ok(())
}
pub fn print_message(&self, message: &str) -> io::Result<()> {
if self.json {
let msg = serde_json::json!({ "message": message });
self.print_json(&msg)
} else {
println!("{}", message);
Ok(())
}
}
pub fn print_error(&self, error: &str) -> io::Result<()> {
if self.json {
let err = serde_json::json!({ "error": error });
let json = serde_json::to_string_pretty(&err).map_err(io::Error::other)?;
eprintln!("{}", json);
} else {
eprintln!("Error: {}", error);
}
Ok(())
}
pub fn print_success(&self, message: &str) -> io::Result<()> {
if self.json {
let msg = serde_json::json!({ "status": "success", "message": message });
self.print_json(&msg)
} else {
println!("✓ {}", message);
Ok(())
}
}
pub fn print_info(&self, message: &str) -> io::Result<()> {
if self.json {
let msg = serde_json::json!({ "status": "info", "message": message });
self.print_json(&msg)
} else {
println!("ℹ {}", message);
Ok(())
}
}
}
#[derive(Serialize)]
pub struct BackendListOutput {
pub backends: Vec<String>,
}
#[derive(Serialize)]
pub struct BackendInfoOutput {
pub name: String,
pub version: String,
pub channel_count: u32,
pub supports_canfd: bool,
pub max_bitrate: u32,
pub supported_bitrates: Vec<u32>,
pub filter_count: u32,
}
#[derive(Serialize)]
pub struct MessageOutput {
pub id: String,
pub data: String,
pub timestamp: Option<String>,
pub flags: Vec<String>,
}
impl MessageOutput {
pub fn format_human(&self) -> String {
let mut parts = vec![format!("ID: {}", self.id), format!("Data: {}", self.data)];
if let Some(ref ts) = self.timestamp {
parts.push(format!("Timestamp: {}", ts));
}
if !self.flags.is_empty() {
parts.push(format!("Flags: {}", self.flags.join(", ")));
}
parts.join(" | ")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_output_formatter_creation() {
let formatter = OutputFormatter::new(false);
assert!(!formatter.json);
let formatter = OutputFormatter::new(true);
assert!(formatter.json);
}
#[test]
fn test_backend_list_output() {
let output = BackendListOutput {
backends: vec!["tscan".to_string(), "demo".to_string()],
};
let json = serde_json::to_string(&output).unwrap();
assert!(json.contains("tscan"));
assert!(json.contains("demo"));
}
#[test]
fn test_message_output_format() {
let msg = MessageOutput {
id: "0x123".to_string(),
data: "01 02 03".to_string(),
timestamp: Some("1234567890".to_string()),
flags: vec!["FD".to_string(), "BRS".to_string()],
};
let formatted = msg.format_human();
assert!(formatted.contains("ID: 0x123"));
assert!(formatted.contains("Data: 01 02 03"));
assert!(formatted.contains("Timestamp: 1234567890"));
assert!(formatted.contains("Flags: FD, BRS"));
}
}