use std::io::{self, IsTerminal, Write};
use clap::ValueEnum;
use serde::Serialize;
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub enum OutputFormat {
Json,
Ndjson,
Yaml,
Table,
Text,
}
impl OutputFormat {
pub fn resolve_oneshot(user: Option<Self>) -> Self {
match user {
Some(fmt) => fmt,
None if io::stdout().is_terminal() => Self::Table,
None => Self::Json,
}
}
pub fn resolve_stream(user: Option<Self>) -> Self {
match user {
Some(fmt) => fmt,
None if io::stdout().is_terminal() => Self::Text,
None => Self::Ndjson,
}
}
}
pub fn emit_value<T: Serialize>(fmt: OutputFormat, value: &T) -> io::Result<()> {
let stdout = io::stdout();
let mut lock = stdout.lock();
match fmt {
OutputFormat::Json | OutputFormat::Table => {
serde_json::to_writer_pretty(&mut lock, value).map_err(io::Error::other)?;
writeln!(&mut lock)?;
}
OutputFormat::Ndjson => {
serde_json::to_writer(&mut lock, value).map_err(io::Error::other)?;
writeln!(&mut lock)?;
}
OutputFormat::Yaml => {
serde_yaml::to_writer(&mut lock, value).map_err(io::Error::other)?;
}
OutputFormat::Text => {
let s = serde_json::to_string_pretty(value).map_err(io::Error::other)?;
writeln!(&mut lock, "{}", s)?;
}
}
Ok(())
}
pub fn emit_stream_row<T: Serialize>(fmt: OutputFormat, row: &T) -> io::Result<()> {
let stdout = io::stdout();
let mut lock = stdout.lock();
match fmt {
OutputFormat::Ndjson | OutputFormat::Json => {
serde_json::to_writer(&mut lock, row).map_err(io::Error::other)?;
writeln!(&mut lock)?;
}
OutputFormat::Yaml => {
writeln!(&mut lock, "---")?;
serde_yaml::to_writer(&mut lock, row).map_err(io::Error::other)?;
}
OutputFormat::Text | OutputFormat::Table => {
let s = serde_json::to_string(row).map_err(io::Error::other)?;
writeln!(&mut lock, "{}", s)?;
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn resolve_honours_explicit_choice() {
assert_eq!(
OutputFormat::resolve_oneshot(Some(OutputFormat::Yaml)),
OutputFormat::Yaml
);
assert_eq!(
OutputFormat::resolve_stream(Some(OutputFormat::Json)),
OutputFormat::Json
);
}
}