1mod csv_fmt;
7mod json;
8mod table;
9
10use serde::Serialize;
11
12use crate::error::Result;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize, Default)]
16#[serde(rename_all = "lowercase")]
17pub enum OutputFormat {
18 #[default]
20 Table,
21 Json,
23 Csv,
25}
26
27impl std::fmt::Display for OutputFormat {
28 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29 match self {
30 Self::Table => write!(f, "table"),
31 Self::Json => write!(f, "json"),
32 Self::Csv => write!(f, "csv"),
33 }
34 }
35}
36
37impl std::str::FromStr for OutputFormat {
38 type Err = crate::error::MktError;
39
40 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
41 match s.to_lowercase().as_str() {
42 "table" => Ok(Self::Table),
43 "json" => Ok(Self::Json),
44 "csv" => Ok(Self::Csv),
45 other => Err(crate::error::MktError::ValidationError {
46 field: "output".into(),
47 message: format!("Unknown output format: '{other}'. Use table, json, or csv."),
48 }),
49 }
50 }
51}
52
53pub trait Formattable {
55 fn headers() -> Vec<String>;
57 fn row(&self) -> Vec<String>;
59}
60
61pub fn format_output<T: Formattable + Serialize>(
67 items: &[T],
68 format: OutputFormat,
69) -> Result<String> {
70 match format {
71 OutputFormat::Table => Ok(table::format_table::<T>(items)),
72 OutputFormat::Json => json::format_json(items),
73 OutputFormat::Csv => csv_fmt::format_csv::<T>(items),
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80
81 #[test]
82 fn output_format_display() {
83 assert_eq!(OutputFormat::Table.to_string(), "table");
84 assert_eq!(OutputFormat::Json.to_string(), "json");
85 assert_eq!(OutputFormat::Csv.to_string(), "csv");
86 }
87
88 #[test]
89 #[allow(clippy::expect_used)]
90 fn output_format_from_str() {
91 assert_eq!(
92 "table".parse::<OutputFormat>().expect("ok"),
93 OutputFormat::Table
94 );
95 assert_eq!(
96 "JSON".parse::<OutputFormat>().expect("ok"),
97 OutputFormat::Json
98 );
99 assert_eq!(
100 "csv".parse::<OutputFormat>().expect("ok"),
101 OutputFormat::Csv
102 );
103 }
104
105 #[test]
106 fn output_format_from_str_invalid() {
107 assert!("xml".parse::<OutputFormat>().is_err());
108 }
109}