Skip to main content

futucli/
output.rs

1//! 输出格式化:table / json / jsonl
2
3use std::io::{self, Write};
4
5use clap::ValueEnum;
6use serde::Serialize;
7use tabled::{Table, Tabled, settings::Style};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
10pub enum OutputFormat {
11    /// 人类可读表格
12    Table,
13    /// 单个 JSON 数组
14    Json,
15    /// 每行一个 JSON 对象(适合管道处理)
16    Jsonl,
17    /// Markdown 表格(适合粘贴到 review / codex 文档)
18    #[value(alias = "md")]
19    Markdown,
20}
21
22impl OutputFormat {
23    /// 以 table 优先输出;如果 fmt != Table 则走 json/jsonl。
24    pub fn print_rows<R, S>(&self, rows: &[R], serializable: &[S]) -> io::Result<()>
25    where
26        R: Tabled,
27        S: Serialize,
28    {
29        match self {
30            OutputFormat::Table | OutputFormat::Markdown => {
31                if rows.is_empty() {
32                    println!("(empty)");
33                    return Ok(());
34                }
35                let mut table = Table::new(rows);
36                if matches!(self, OutputFormat::Markdown) {
37                    table.with(Style::markdown());
38                } else {
39                    table.with(Style::rounded());
40                }
41                println!("{table}");
42                Ok(())
43            }
44            OutputFormat::Json => {
45                let s = serde_json::to_string_pretty(serializable).map_err(io::Error::other)?;
46                println!("{s}");
47                Ok(())
48            }
49            OutputFormat::Jsonl => {
50                let stdout = io::stdout();
51                let mut lock = stdout.lock();
52                for item in serializable {
53                    let s = serde_json::to_string(item).map_err(io::Error::other)?;
54                    writeln!(lock, "{s}")?;
55                }
56                Ok(())
57            }
58        }
59    }
60}