use std::io::IsTerminal;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
use tracing_subscriber::{fmt, EnvFilter};
use crate::cli::GlobalArgs;
use crate::runtime::verdict::VerdictHint;
use crate::ui::color;
pub fn init(g: &GlobalArgs) {
let filter = resolve_filter(g);
let ansi = should_color(g.no_color);
let is_tty = std::io::stderr().is_terminal();
let result = if is_tty {
tracing_subscriber::registry()
.with(filter)
.with(
fmt::layer()
.with_writer(std::io::stderr)
.with_ansi(ansi)
.with_target(false),
)
.try_init()
} else {
tracing_subscriber::registry()
.with(filter)
.with(fmt::layer().json().with_writer(std::io::stderr))
.try_init()
};
let _ = result;
}
fn resolve_filter(g: &GlobalArgs) -> EnvFilter {
if let Some(env) = std::env::var("RUST_LOG").ok().filter(|s| !s.is_empty()) {
if let Ok(f) = EnvFilter::try_new(&env) {
return f;
}
}
let level = if g.debug {
"openlatch_provider=trace,info"
} else if g.verbose {
"openlatch_provider=debug,info"
} else {
"info"
};
EnvFilter::try_new(level).unwrap_or_else(|_| EnvFilter::new("info"))
}
pub fn should_color(no_color_flag: bool) -> bool {
if no_color_flag {
return false;
}
if std::env::var_os("NO_COLOR").is_some() {
return false;
}
if std::env::var_os("FORCE_COLOR").is_some() {
return true;
}
std::io::stderr().is_terminal()
}
pub fn verdict_display(hint: Option<VerdictHint>, ansi: bool) -> String {
match hint {
Some(VerdictHint::Allow) => color::green("allow", ansi),
Some(VerdictHint::Approve) => color::yellow("approve", ansi),
Some(VerdictHint::Deny) => color::red("deny", ansi),
None => color::dim("unknown", ansi),
}
}
pub fn verdict_display_str(token: Option<&str>, ansi: bool) -> String {
let Some(token) = token else {
return color::dim("—", ansi);
};
match token {
"allow" => color::green(token, ansi),
"deny" => color::red(token, ansi),
"approve" | "flag" => color::yellow(token, ansi),
other => other.to_string(),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn verdict_display_allow_is_green_when_ansi() {
let out = verdict_display(Some(VerdictHint::Allow), true);
assert!(out.contains("\x1b[32m"), "expected green ANSI for allow");
assert!(out.contains("allow"));
}
#[test]
fn verdict_display_deny_is_red_when_ansi() {
let out = verdict_display(Some(VerdictHint::Deny), true);
assert!(out.contains("\x1b[31m"), "expected red ANSI for deny");
assert!(out.contains("deny"));
}
#[test]
fn verdict_display_approve_is_yellow_when_ansi() {
let out = verdict_display(Some(VerdictHint::Approve), true);
assert!(out.contains("\x1b[33m"), "expected yellow ANSI for approve");
}
#[test]
fn verdict_display_plain_without_ansi() {
assert_eq!(verdict_display(Some(VerdictHint::Allow), false), "allow");
}
#[test]
fn verdict_display_missing_hint_renders_unknown() {
assert_eq!(verdict_display(None, false), "unknown");
}
#[test]
fn verdict_display_str_flag_is_yellow() {
let out = verdict_display_str(Some("flag"), true);
assert!(out.contains("\x1b[33m"));
assert!(out.contains("flag"));
}
#[test]
fn resolve_filter_falls_back_to_info_on_default() {
let g = GlobalArgs::default();
let _f = resolve_filter(&g);
}
#[test]
fn should_color_no_color_flag_wins() {
assert!(!should_color(true));
}
}