use std::io::Write;
use colored::Colorize;
use serde::Serialize;
use crate::types::OutputFormat;
pub(super) fn write_json<W: Write + ?Sized>(value: &(impl Serialize + ?Sized), out: &mut W) {
let _ = writeln!(
out,
"{}",
serde_json::to_string_pretty(value).expect("serializable to JSON")
);
}
pub(super) fn write_formatted<T, W>(
value: &T,
format: OutputFormat,
out: &mut W,
table_fn: impl FnOnce(&T, &mut W),
) where
T: Serialize + ?Sized,
W: Write + ?Sized,
{
match format {
OutputFormat::Json => write_json(value, out),
OutputFormat::Table => table_fn(value, out),
}
}
pub(super) fn write_field<W: Write + ?Sized>(out: &mut W, label: &str, value: &str) {
let _ = writeln!(out, " {label:<12} {value}");
}
pub(super) fn write_optional_field<W: Write + ?Sized>(
out: &mut W,
label: &str,
value: Option<&str>,
) {
let _ = writeln!(out, " {label:<12} {}", value.unwrap_or("-"));
}
pub(super) fn write_list_field<W: Write + ?Sized>(out: &mut W, label: &str, items: &[String]) {
if !items.is_empty() {
let _ = writeln!(out, " {label:<12} {}", items.join(", "));
}
}
pub(super) fn write_bool_field<W: Write + ?Sized>(out: &mut W, label: &str, value: bool) {
let _ = writeln!(out, " {label:<12} {}", yes_no(value));
}
pub(super) const DIVIDER_WIDTH: usize = 60;
pub(crate) fn write_divider<W: Write + ?Sized>(out: &mut W) {
let _ = writeln!(out, "{}", "─".repeat(DIVIDER_WIDTH));
}
pub(super) fn yes_no(value: bool) -> &'static str {
if value {
"Yes"
} else {
"No"
}
}
pub(super) fn opt_yes_no(value: Option<bool>) -> &'static str {
match value {
Some(true) => "Yes",
Some(false) => "No",
None => "-",
}
}
pub(super) fn truncate(s: &str, max_chars: usize) -> String {
if s.chars().count() > max_chars {
let truncated: String = s.chars().take(max_chars - 3).collect();
format!("{truncated}...")
} else {
s.to_string()
}
}
pub(super) fn shorten_email(email: &str) -> String {
if let Some(at) = email.find('@') {
email[..at].to_string()
} else {
email.to_string()
}
}
pub(super) fn colorize_status(status: &str) -> String {
match status.to_uppercase().as_str() {
"NEW" | "UNCONFIRMED" => status.green().to_string(),
"ASSIGNED" | "IN_PROGRESS" => status.yellow().to_string(),
"RESOLVED" | "VERIFIED" | "CLOSED" => status.red().to_string(),
_ => status.to_string(),
}
}
pub(super) fn mask_api_key(key: &str) -> String {
if key.len() > 8 {
format!("{}...", &key[..8])
} else {
"***".into()
}
}
#[cfg(test)]
#[path = "formatting_tests.rs"]
mod tests;