use serde_json::{json, Value};
use std::io::IsTerminal;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OutputFormat {
Text,
Json,
Pretty,
}
impl OutputFormat {
#[must_use]
pub fn parse(s: &str) -> Option<Self> {
match s.to_lowercase().as_str() {
"text" => Some(Self::Text),
"json" => Some(Self::Json),
"pretty" => Some(Self::Pretty),
_ => None,
}
}
}
#[must_use]
pub fn resolve_format(flag: Option<&str>) -> OutputFormat {
if let Some(fmt) = flag {
if let Some(parsed) = OutputFormat::parse(fmt) {
return parsed;
}
return OutputFormat::Text;
}
if let Ok(env_format) = std::env::var("FORMAT") {
if let Some(parsed) = OutputFormat::parse(&env_format) {
return parsed;
}
return OutputFormat::Text;
}
if std::io::stdout().is_terminal() {
OutputFormat::Pretty
} else {
OutputFormat::Text
}
}
#[must_use]
pub fn json_envelope(key: &str, data: Value, advice: Vec<String>) -> Value {
json!({
key: data,
"advice": advice,
})
}
#[must_use]
pub fn text_record(fields: &[&str]) -> String {
fields.join(" ")
}
#[cfg(test)]
#[allow(unsafe_code)] mod tests {
use super::*;
#[test]
fn test_parse_valid_formats() {
assert_eq!(OutputFormat::parse("text"), Some(OutputFormat::Text));
assert_eq!(OutputFormat::parse("json"), Some(OutputFormat::Json));
assert_eq!(OutputFormat::parse("pretty"), Some(OutputFormat::Pretty));
assert_eq!(OutputFormat::parse("TEXT"), Some(OutputFormat::Text));
assert_eq!(OutputFormat::parse("Json"), Some(OutputFormat::Json));
assert_eq!(OutputFormat::parse("PRETTY"), Some(OutputFormat::Pretty));
}
#[test]
fn test_parse_invalid_format() {
assert_eq!(OutputFormat::parse("unknown"), None);
assert_eq!(OutputFormat::parse("xml"), None);
assert_eq!(OutputFormat::parse(""), None);
}
#[test]
fn test_resolve_format_with_flag() {
assert_eq!(resolve_format(Some("json")), OutputFormat::Json);
assert_eq!(resolve_format(Some("text")), OutputFormat::Text);
assert_eq!(resolve_format(Some("pretty")), OutputFormat::Pretty);
}
#[test]
fn test_resolve_format_unknown_flag() {
assert_eq!(resolve_format(Some("unknown")), OutputFormat::Text);
assert_eq!(resolve_format(Some("xml")), OutputFormat::Text);
}
#[test]
fn test_resolve_format_with_env() {
unsafe {
std::env::remove_var("FORMAT");
}
unsafe {
std::env::set_var("FORMAT", "json");
}
assert_eq!(resolve_format(None), OutputFormat::Json);
unsafe {
std::env::set_var("FORMAT", "text");
}
assert_eq!(resolve_format(None), OutputFormat::Text);
unsafe {
std::env::set_var("FORMAT", "pretty");
}
assert_eq!(resolve_format(None), OutputFormat::Pretty);
unsafe {
std::env::set_var("FORMAT", "unknown");
}
assert_eq!(resolve_format(None), OutputFormat::Text);
unsafe {
std::env::remove_var("FORMAT");
}
}
#[test]
fn test_resolve_format_flag_overrides_env() {
unsafe {
std::env::set_var("FORMAT", "json");
}
assert_eq!(resolve_format(Some("text")), OutputFormat::Text);
unsafe {
std::env::remove_var("FORMAT");
}
}
#[test]
fn test_json_envelope_with_data() {
let data = json!({"id": "agent-123", "status": "running"});
let advice = vec!["Use --json for structured output".to_string()];
let result = json_envelope("agent", data.clone(), advice.clone());
assert_eq!(result["agent"], data);
assert_eq!(result["advice"], json!(advice));
}
#[test]
fn test_json_envelope_empty_advice() {
let data = json!({"id": "agent-123"});
let result = json_envelope("agent", data.clone(), vec![]);
assert_eq!(result["agent"], data);
assert_eq!(result["advice"], json!([]));
}
#[test]
fn test_json_envelope_multiple_advice() {
let data = json!(null);
let advice = vec![
"First tip".to_string(),
"Second tip".to_string(),
"Third tip".to_string(),
];
let result = json_envelope("result", data, advice.clone());
assert_eq!(result["advice"], json!(advice));
}
#[test]
fn test_text_record_simple() {
let result = text_record(&["agent-abc", "running", "bash"]);
assert_eq!(result, "agent-abc running bash");
}
#[test]
fn test_text_record_with_keyvalue() {
let result = text_record(&["agent-abc", "running", "bash", "pid=1234"]);
assert_eq!(result, "agent-abc running bash pid=1234");
}
#[test]
fn test_text_record_single_field() {
let result = text_record(&["agent-abc"]);
assert_eq!(result, "agent-abc");
}
#[test]
fn test_text_record_empty() {
let result = text_record(&[]);
assert_eq!(result, "");
}
#[test]
fn test_text_record_fields_with_spaces() {
let result = text_record(&["field one", "field two", "field three"]);
assert_eq!(result, "field one field two field three");
}
}