use serde::Serialize;
use slumber_config::Config;
use slumber_core::util::confirm;
use std::{error::Error, io, iter, path::Path, process::ExitCode};
pub fn print_error(error: &anyhow::Error) {
eprintln!("{error}");
error
.chain()
.skip(1)
.for_each(|cause| eprintln!(" {cause}"));
}
pub fn print_table<const N: usize>(header: [&str; N], rows: &[[String; N]]) {
let mut widths = [0; N];
for column in 0..N {
widths[column] = iter::once(header[column].len())
.chain(rows.iter().map(|row| row[column].len()))
.max()
.unwrap_or_default()
+ 1; }
for (header, width) in header.into_iter().zip(widths.iter()) {
print!("{header:<width$}");
}
println!();
for row in rows {
for (cell, width) in row.iter().zip(widths) {
print!("{cell:<width$}");
}
println!();
}
}
pub fn print_yaml<T: Serialize>(value: &T) -> anyhow::Result<()> {
serde_yaml::to_writer(io::stdout(), value).map_err(anyhow::Error::from)
}
pub fn edit_and_validate<T, E: 'static + Error + Send + Sync>(
config: &Config,
path: &Path,
validate: impl Fn() -> Result<T, E>,
) -> anyhow::Result<ExitCode> {
let editor = config.editor()?;
loop {
let status = editor.open(path).spawn()?.wait()?;
if let Err(error) = validate() {
print_error(&error.into());
if confirm(format!(
"{path} is invalid, would you like to reopen it?",
path = path.display(),
)) {
continue;
}
}
let code = status.code().and_then(|code| u8::try_from(code).ok());
return Ok(code.map(ExitCode::from).unwrap_or(ExitCode::FAILURE));
}
}