Skip to main content

alopex_cli/output/
formatter.rs

1//! Formatter trait and factory function
2//!
3//! Defines the common interface for all output formatters.
4
5use std::io::Write;
6
7use crate::cli::OutputFormat;
8use crate::error::Result;
9use crate::models::{Column, Row};
10
11use super::csv::CsvFormatter;
12use super::json::JsonFormatter;
13use super::jsonl::JsonlFormatter;
14use super::table::TableFormatter;
15use super::tsv::TsvFormatter;
16
17/// Trait for output formatters.
18///
19/// All formatters must be Send + Sync to allow use in multi-threaded contexts.
20pub trait Formatter: Send + Sync {
21    /// Write the header (column names) to the output.
22    ///
23    /// For streaming formats (json, jsonl, csv, tsv), this is called immediately.
24    /// For buffered formats (table), this is called after buffer evaluation.
25    fn write_header(&mut self, writer: &mut dyn Write, columns: &[Column]) -> Result<()>;
26
27    /// Write a single row to the output.
28    fn write_row(&mut self, writer: &mut dyn Write, row: &Row) -> Result<()>;
29
30    /// Write the footer to the output.
31    ///
32    /// For json format, this closes the array. For others, this may be a no-op.
33    fn write_footer(&mut self, writer: &mut dyn Write) -> Result<()>;
34
35    /// Returns true if this formatter supports streaming output.
36    ///
37    /// Streaming formats (json, jsonl, csv, tsv) write rows immediately.
38    /// Non-streaming formats (table) buffer rows before output.
39    fn supports_streaming(&self) -> bool;
40}
41
42/// Create a formatter for the specified output format.
43pub fn create_formatter(format: OutputFormat) -> Box<dyn Formatter> {
44    match format {
45        OutputFormat::Table => Box::new(TableFormatter::new()),
46        OutputFormat::Json => Box::new(JsonFormatter::new()),
47        OutputFormat::Jsonl => Box::new(JsonlFormatter::new()),
48        OutputFormat::Csv => Box::new(CsvFormatter::new()),
49        OutputFormat::Tsv => Box::new(TsvFormatter::new()),
50    }
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56
57    #[test]
58    fn test_create_formatter_table() {
59        let formatter = create_formatter(OutputFormat::Table);
60        assert!(!formatter.supports_streaming());
61    }
62
63    #[test]
64    fn test_create_formatter_json() {
65        let formatter = create_formatter(OutputFormat::Json);
66        assert!(formatter.supports_streaming());
67    }
68
69    #[test]
70    fn test_create_formatter_jsonl() {
71        let formatter = create_formatter(OutputFormat::Jsonl);
72        assert!(formatter.supports_streaming());
73    }
74
75    #[test]
76    fn test_create_formatter_csv() {
77        let formatter = create_formatter(OutputFormat::Csv);
78        assert!(formatter.supports_streaming());
79    }
80
81    #[test]
82    fn test_create_formatter_tsv() {
83        let formatter = create_formatter(OutputFormat::Tsv);
84        assert!(formatter.supports_streaming());
85    }
86}