use clap::ValueEnum;
use serde::Serialize;
use tabled::Tabled;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, ValueEnum)]
pub enum OutputMode {
#[default]
Text,
Json,
}
impl OutputMode {
#[must_use]
pub fn from_args_os() -> Self {
Self::from_args(std::env::args_os())
}
fn from_args<I>(args: I) -> Self
where
I: IntoIterator<Item = std::ffi::OsString>,
{
let mut iter = args.into_iter();
while let Some(arg) = iter.next() {
let Some(s) = arg.to_str() else { continue };
if let Some(rest) = s.strip_prefix("--output=") {
return Self::parse_value(rest).unwrap_or_default();
}
if s == "--output" {
if let Some(next) = iter.next() {
if let Some(value) = next.to_str() {
return Self::parse_value(value).unwrap_or_default();
}
}
return Self::default();
}
}
Self::default()
}
fn parse_value(value: &str) -> Option<Self> {
match value {
"text" => Some(Self::Text),
"json" => Some(Self::Json),
_ => None,
}
}
}
#[must_use]
pub fn strip_global_output(args: Vec<std::ffi::OsString>) -> Vec<std::ffi::OsString> {
let mut out = Vec::with_capacity(args.len());
let mut iter = args.into_iter();
while let Some(arg) = iter.next() {
let Some(s) = arg.to_str() else {
out.push(arg);
continue;
};
if s.starts_with("--output=") {
continue;
}
if s == "--output" {
iter.next();
continue;
}
out.push(arg);
}
out
}
pub fn output<R>(mode: OutputMode, rows: &[R]) -> Result<(), rtb_tui::RenderError>
where
R: Tabled + Serialize,
{
let rendered = match mode {
OutputMode::Text => rtb_tui::render_table(rows),
OutputMode::Json => rtb_tui::render_json(rows)?,
};
print!("{rendered}");
Ok(())
}
#[cfg(test)]
mod tests {
use super::OutputMode;
fn parse(args: &[&str]) -> OutputMode {
OutputMode::from_args(args.iter().map(|s| std::ffi::OsString::from(*s)))
}
#[test]
fn default_when_flag_absent() {
assert_eq!(parse(&["mytool", "subcommand"]), OutputMode::Text);
}
#[test]
fn parses_space_separated_text() {
assert_eq!(parse(&["mytool", "--output", "text", "sub"]), OutputMode::Text);
}
#[test]
fn parses_space_separated_json() {
assert_eq!(parse(&["mytool", "--output", "json", "sub"]), OutputMode::Json);
}
#[test]
fn parses_eq_separated_json() {
assert_eq!(parse(&["mytool", "sub", "--output=json"]), OutputMode::Json);
}
#[test]
fn unknown_value_falls_back_to_default() {
assert_eq!(parse(&["mytool", "--output", "yaml"]), OutputMode::Text);
}
#[test]
fn missing_value_falls_back_to_default() {
assert_eq!(parse(&["mytool", "--output"]), OutputMode::Text);
}
#[test]
fn flag_after_subcommand_works() {
assert_eq!(parse(&["mytool", "subcmd", "--output", "json"]), OutputMode::Json);
}
}