use std::path::Path;
use incise_core::json::dumps_str;
use incise_core::{Fmt, FrontState, TableRows};
pub const EXIT_OK: i32 = 0;
pub const EXIT_REFUSED: i32 = 1;
pub const EXIT_USAGE: i32 = 2;
pub const EXIT_STALE: i32 = 3;
pub struct Format {
pub json: bool,
pub quiet: bool,
}
pub fn success(
f: &Format,
description: &str,
hash: &str,
changed: bool,
written: bool,
path: &Path,
) -> i32 {
if f.json {
println!(
"{{\"ok\": true, \"description\": {}, \"hash\": {}, \"changed\": {}, \
\"written\": {}, \"path\": {}}}",
dumps_str(description),
dumps_str(hash),
changed,
written,
dumps_str(&path.display().to_string()),
);
} else if !f.quiet {
println!("{description}");
if changed && !written {
eprintln!("dry run: {} not written", path.display());
}
}
EXIT_OK
}
pub fn view(f: &Format, text: &str, hash: &str, path: &Path) -> i32 {
if f.json {
println!(
"{{\"ok\": true, \"text\": {}, \"hash\": {}, \"path\": {}}}",
dumps_str(text),
dumps_str(hash),
dumps_str(&path.display().to_string()),
);
} else {
println!("{text}");
if !f.quiet {
eprintln!("hash: {hash}");
}
}
EXIT_OK
}
pub fn rows_view(f: &Format, text: &str, got: &TableRows, hash: &str, path: &Path) -> i32 {
if !f.json {
return view(f, text, hash, path);
}
println!(
"{{\"ok\": true, \"text\": {}, \"rows\": {{\"heading\": {}, \"columns\": {}, \
\"rows\": {}, \"matched\": {}, \"total\": {}}}, \"hash\": {}, \"path\": {}}}",
dumps_str(text),
dumps_str(&got.heading),
dumps_strs(&got.columns),
dumps_rows(&got.rows),
got.matched,
got.total,
dumps_str(hash),
dumps_str(&path.display().to_string()),
);
EXIT_OK
}
pub fn keys_view(f: &Format, text: &str, got: &FrontState, hash: &str, path: &Path) -> i32 {
if !f.json {
return view(f, text, hash, path);
}
let keys: Vec<String> = got
.keys
.iter()
.map(|k| {
format!(
"{{\"path\": {}, \"kind\": {}, \"value\": {}, \"lines\": {}}}",
dumps_str(&k.path),
dumps_str(k.kind),
dumps_str(&k.value),
k.lines,
)
})
.collect();
println!(
"{{\"ok\": true, \"text\": {}, \"frontmatter\": {{\"state\": {}, \"format\": {}, \
\"keys\": [{}]}}, \"hash\": {}, \"path\": {}}}",
dumps_str(text),
dumps_str(got.state),
match got.format {
Some(Fmt::Yaml) => "\"yaml\"".to_string(),
Some(Fmt::Toml) => "\"toml\"".to_string(),
None => "null".to_string(),
},
keys.join(", "),
dumps_str(hash),
dumps_str(&path.display().to_string()),
);
EXIT_OK
}
fn dumps_strs(items: &[String]) -> String {
let inner: Vec<String> = items.iter().map(|s| dumps_str(s)).collect();
format!("[{}]", inner.join(", "))
}
fn dumps_rows(rows: &[Vec<String>]) -> String {
let inner: Vec<String> = rows.iter().map(|r| dumps_strs(r)).collect();
format!("[{}]", inner.join(", "))
}
pub fn refusal(f: &Format, message: &str) -> i32 {
if f.json {
println!("{{\"ok\": false, \"error\": {}}}", dumps_str(message));
} else {
eprintln!("Error: {message}");
}
EXIT_REFUSED
}
pub fn stale(f: &Format, want: &str, got: &str, path: &Path) -> i32 {
let message = format!(
"{} has changed since it was read. Expected a hash starting {want}, found {got}. \
Read it again before editing.",
path.display()
);
if f.json {
println!(
"{{\"ok\": false, \"stale\": true, \"error\": {}, \"hash\": {}}}",
dumps_str(&message),
dumps_str(got),
);
} else {
eprintln!("Error: {message}");
}
EXIT_STALE
}
pub fn usage(f: &Format, message: &str) -> i32 {
if f.json {
println!(
"{{\"ok\": false, \"usage\": true, \"error\": {}}}",
dumps_str(message)
);
} else {
eprintln!("incise: {message}");
}
EXIT_USAGE
}