use serde::Serialize;
use crate::api::client::ApiError;
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum Format {
Human,
Json,
}
impl Format {
pub fn is_json(self) -> bool {
matches!(self, Format::Json)
}
}
pub fn chosen(flag: bool) -> Format {
if flag || wants_json() {
Format::Json
} else {
Format::Human
}
}
fn wants_json() -> bool {
std::env::var("FUNDAIA_JSON").is_ok_and(|value| truthy(&value))
}
fn truthy(value: &str) -> bool {
let value = value.trim();
!value.is_empty() && value != "0"
}
pub fn emit<T: Serialize>(value: &T) {
println!(
"{}",
serde_json::to_string(value).expect("the DTOs serialise to JSON by construction")
);
}
pub fn emit_error(error: &anyhow::Error, hint: Option<&str>) {
let mut body = serde_json::Map::new();
body.insert("message".into(), error.to_string().into());
match error.downcast_ref::<ApiError>() {
Some(api) => {
body.insert("code".into(), api.code.clone().into());
body.insert("status".into(), api.status.as_u16().into());
}
None => {
body.insert("code".into(), "cli_error".into());
}
}
let causes: Vec<String> = error
.chain()
.skip(1)
.map(|cause| cause.to_string())
.collect();
if !causes.is_empty() {
body.insert("causes".into(), causes.into());
}
if let Some(hint) = hint {
body.insert("hint".into(), hint.into());
}
eprintln!(
"{}",
serde_json::Value::Object(serde_json::Map::from_iter([(
"error".to_string(),
serde_json::Value::Object(body)
),]))
);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_should_answer_json_when_the_flag_is_given() {
assert!(chosen(true).is_json());
}
#[test]
fn it_should_be_turned_on_by_a_one() {
assert!(truthy("1"));
}
#[test]
fn it_should_not_be_turned_on_by_a_zero() {
assert!(!truthy("0"));
}
#[test]
fn it_should_not_be_turned_on_by_an_empty_value() {
assert!(!truthy(""));
}
}