use serde::{Deserialize, Serialize};
use crate::{Envelope, FaceError};
mod delimited;
mod human;
mod json_out;
mod markdown;
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq, Hash)]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub enum OutputFormat {
#[default]
Human,
Json,
JsonFlat,
JsonlItems,
Tsv,
Csv,
Markdown,
}
#[derive(Debug, Clone, Default)]
#[non_exhaustive]
pub struct RenderOptions {
pub color: bool,
pub width: Option<usize>,
}
pub fn render(
envelope: &Envelope,
format: OutputFormat,
opts: &RenderOptions,
) -> Result<String, FaceError> {
match format {
OutputFormat::Human => Ok(human::render(envelope, opts)),
OutputFormat::Json => json_out::render_nested(envelope),
OutputFormat::JsonFlat => json_out::render_flat(envelope),
OutputFormat::JsonlItems => Ok(json_out::render_jsonl_items(envelope)),
OutputFormat::Tsv => Ok(delimited::render_tsv(envelope)),
OutputFormat::Csv => Ok(delimited::render_csv(envelope)),
OutputFormat::Markdown => Ok(markdown::render(envelope)),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn round_trip_format_serde() {
let cases = [
(OutputFormat::Human, "\"human\""),
(OutputFormat::Json, "\"json\""),
(OutputFormat::JsonFlat, "\"json-flat\""),
(OutputFormat::JsonlItems, "\"jsonl-items\""),
(OutputFormat::Tsv, "\"tsv\""),
(OutputFormat::Csv, "\"csv\""),
(OutputFormat::Markdown, "\"markdown\""),
];
for (fmt, wire) in cases {
let s = serde_json::to_string(&fmt).unwrap();
assert_eq!(s, wire, "serialize {fmt:?}");
let back: OutputFormat = serde_json::from_str(wire).unwrap();
assert_eq!(back, fmt, "deserialize {wire}");
}
}
#[test]
fn default_is_human() {
assert_eq!(OutputFormat::default(), OutputFormat::Human);
}
#[test]
fn render_dispatches_to_each_format() {
let env = Envelope::default();
let opts = RenderOptions::default();
for fmt in [
OutputFormat::Human,
OutputFormat::Json,
OutputFormat::JsonFlat,
OutputFormat::JsonlItems,
OutputFormat::Tsv,
OutputFormat::Csv,
OutputFormat::Markdown,
] {
let _ = render(&env, fmt, &opts).unwrap();
}
}
}