Skip to main content

raps_cli/output/
mod.rs

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    /// Determine the output format to use based on the user's choice and the environment.
50    pub fn determine(explicit: Option<OutputFormat>) -> Self {
51        if let Some(format) = explicit {
52            return format;
53        }
54
55        // Check environment variable
56        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;