1use anyhow::Result;
2use clap::ValueEnum;
3use serde::Serialize;
4use std::fmt::Display;
5use std::io::IsTerminal;
6use std::str::FromStr;
7
8pub mod formatter;
9use crate::output::formatter::OutputFormatter;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum, Default)]
12pub enum OutputFormat {
13 #[default]
14 Table,
15 Json,
16 Yaml,
17 Csv,
18 Plain,
19}
20
21impl Display for OutputFormat {
22 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23 match self {
24 OutputFormat::Table => write!(f, "table"),
25 OutputFormat::Json => write!(f, "json"),
26 OutputFormat::Yaml => write!(f, "yaml"),
27 OutputFormat::Csv => write!(f, "csv"),
28 OutputFormat::Plain => write!(f, "plain"),
29 }
30 }
31}
32
33impl FromStr for OutputFormat {
34 type Err = String;
35
36 fn from_str(s: &str) -> Result<Self, Self::Err> {
37 match s.to_lowercase().as_str() {
38 "table" => Ok(OutputFormat::Table),
39 "json" => Ok(OutputFormat::Json),
40 "yaml" => Ok(OutputFormat::Yaml),
41 "csv" => Ok(OutputFormat::Csv),
42 "plain" => Ok(OutputFormat::Plain),
43 _ => Err(format!("Invalid format: {}", s)),
44 }
45 }
46}
47
48impl OutputFormat {
49 pub fn determine(explicit: Option<OutputFormat>) -> Self {
51 if let Some(format) = explicit {
52 return format;
53 }
54
55 if let Ok(env_format) = std::env::var("RAPS_OUTPUT_FORMAT")
57 && let Ok(format) = <OutputFormat as FromStr>::from_str(&env_format)
58 {
59 return format;
60 }
61
62 if !std::io::stdout().is_terminal() {
63 return OutputFormat::Json;
64 }
65
66 OutputFormat::Table
67 }
68
69 pub fn supports_colors(&self) -> bool {
70 matches!(self, OutputFormat::Table)
71 }
72
73 pub fn write<T: Serialize>(&self, data: &T) -> Result<()> {
74 let mut stdout = std::io::stdout();
75 OutputFormatter::print_output(data, *self, &mut stdout)
76 }
77
78 pub fn write_message(&self, message: &str) -> Result<()> {
79 match self {
80 OutputFormat::Table | OutputFormat::Plain => {
81 println!("{}", message);
82 Ok(())
83 }
84 OutputFormat::Json => {
85 #[derive(Serialize)]
86 struct Message {
87 message: String,
88 }
89 self.write(&Message {
90 message: message.to_string(),
91 })
92 }
93 OutputFormat::Yaml => {
94 #[derive(Serialize)]
95 struct Message {
96 message: String,
97 }
98 self.write(&Message {
99 message: message.to_string(),
100 })
101 }
102 OutputFormat::Csv => {
103 println!("{}", message);
104 Ok(())
105 }
106 }
107 }
108}
109
110#[cfg(test)]
111mod tests;