Skip to main content

vantage_cli_util/output/
mod.rs

1//! Output formatters for CLI results.
2//!
3//! The CLI doubles as a test-scenario runner across persistences, so output
4//! must be both human-readable (default `table`, ANSI styled via
5//! [`crate::table_display`]) and machine-comparable. JSON variants are the
6//! ergonomic-but-lossy options; `cbor-diag` (RFC 8949 §8 diagnostic
7//! notation) is the lossless form used for golden test fixtures.
8
9use ciborium::Value as CborValue;
10use indexmap::IndexMap;
11use vantage_types::Record;
12
13pub mod cbor_diag;
14pub mod json;
15pub mod ndjson;
16
17/// Output format selector passed into renderers.
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
19pub enum OutputFormat {
20    #[default]
21    Table,
22    Json,
23    Ndjson,
24    CborDiag,
25}
26
27impl OutputFormat {
28    /// Parse the value of a `--format=…` flag. Returns `None` on unknown
29    /// names so call sites can produce their own error text.
30    pub fn parse(s: &str) -> Option<Self> {
31        match s {
32            "table" => Some(Self::Table),
33            "json" => Some(Self::Json),
34            "ndjson" => Some(Self::Ndjson),
35            "cbor-diag" => Some(Self::CborDiag),
36            _ => None,
37        }
38    }
39}
40
41/// Render a list of records to a String in the chosen non-table format.
42///
43/// `Table` is handled by `table_display` directly (it needs ANSI/terminal
44/// concerns) and is not produced here — calling `render_list` with
45/// `OutputFormat::Table` is a programming error and returns an empty
46/// string.
47pub fn render_list(format: OutputFormat, records: &IndexMap<String, Record<CborValue>>) -> String {
48    match format {
49        OutputFormat::Table => String::new(),
50        OutputFormat::Json => json::write_list(records),
51        OutputFormat::Ndjson => ndjson::write_list(records),
52        OutputFormat::CborDiag => cbor_diag::write_list(records),
53    }
54}
55
56/// Render a single record to a String in the chosen non-table format.
57pub fn render_record(format: OutputFormat, id: &str, record: &Record<CborValue>) -> String {
58    match format {
59        OutputFormat::Table => String::new(),
60        OutputFormat::Json => json::write_record(id, record),
61        OutputFormat::Ndjson => ndjson::write_record(id, record),
62        OutputFormat::CborDiag => cbor_diag::write_record(id, record),
63    }
64}
65
66/// Render a scalar (aggregate result) to a String.
67pub fn render_scalar(format: OutputFormat, label: &str, value: &CborValue) -> String {
68    match format {
69        OutputFormat::Table => String::new(),
70        OutputFormat::Json => json::write_scalar(label, value),
71        OutputFormat::Ndjson => ndjson::write_scalar(label, value),
72        OutputFormat::CborDiag => cbor_diag::write_scalar(label, value),
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79
80    #[test]
81    fn parse_known_formats() {
82        assert_eq!(OutputFormat::parse("table"), Some(OutputFormat::Table));
83        assert_eq!(OutputFormat::parse("json"), Some(OutputFormat::Json));
84        assert_eq!(OutputFormat::parse("ndjson"), Some(OutputFormat::Ndjson));
85        assert_eq!(
86            OutputFormat::parse("cbor-diag"),
87            Some(OutputFormat::CborDiag)
88        );
89        assert_eq!(OutputFormat::parse("yaml"), None);
90    }
91}