use std::io::{self, IsTerminal, Write};
use anyhow::Result;
use diurn_mic::{MicRegistry, PublishedSource};
use crate::cli::Format;
impl Format {
pub fn resolve(requested: Option<Format>) -> Format {
requested.unwrap_or_else(|| {
if io::stdout().is_terminal() {
Format::Table
} else {
Format::Jsonl
}
})
}
}
pub struct Provenance {
pub origin: String,
pub published: jiff::civil::Date,
pub source: PublishedSource,
pub records: usize,
}
impl Provenance {
pub fn new(origin: impl Into<String>, registry: &MicRegistry) -> Self {
Self {
origin: origin.into(),
published: registry.published(),
source: registry.published_source(),
records: registry.len(),
}
}
}
pub fn banner(p: &Provenance, quiet: bool) {
if quiet {
return;
}
let derivation = match p.source {
PublishedSource::Given => "",
PublishedSource::InferredFromEffectiveDate => " (date inferred from file)",
PublishedSource::LatestUpdateInFile => " (date is the latest in file; no pending records)",
};
eprintln!(
"ISO 10383 vintage {} — {} records, {}{}",
p.published, p.records, p.origin, derivation
);
}
pub fn note(quiet: bool, args: std::fmt::Arguments<'_>) {
if !quiet {
eprintln!("{args}");
}
}
macro_rules! note_fmt {
($quiet:expr, $($arg:tt)*) => {
$crate::output::note($quiet, format_args!($($arg)*))
};
}
pub(crate) use note_fmt as note;
pub struct Table {
headers: Vec<String>,
rows: Vec<Vec<String>>,
}
impl Table {
pub fn new(headers: &[&str]) -> Self {
Self {
headers: headers.iter().map(|h| h.to_string()).collect(),
rows: Vec::new(),
}
}
pub fn push(&mut self, row: Vec<String>) {
debug_assert_eq!(row.len(), self.headers.len(), "row width must match header");
self.rows.push(row);
}
pub fn is_empty(&self) -> bool {
self.rows.is_empty()
}
pub fn write(&self, w: &mut impl Write) -> Result<()> {
let mut widths: Vec<usize> = self.headers.iter().map(|h| h.chars().count()).collect();
for row in &self.rows {
for (i, cell) in row.iter().enumerate() {
widths[i] = widths[i].max(cell.chars().count());
}
}
let line = |w: &mut dyn Write, cells: &[String]| -> Result<()> {
let mut out = String::new();
for (i, cell) in cells.iter().enumerate() {
if i > 0 {
out.push_str(" ");
}
out.push_str(cell);
if i + 1 < cells.len() {
let pad = widths[i].saturating_sub(cell.chars().count());
out.extend(std::iter::repeat_n(' ', pad));
}
}
writeln!(w, "{}", out.trim_end())?;
Ok(())
};
line(w, &self.headers)?;
let rule: Vec<String> = widths.iter().map(|n| "-".repeat(*n)).collect();
line(w, &rule)?;
for row in &self.rows {
line(w, row)?;
}
Ok(())
}
}