use std::collections::HashSet;
use std::sync::OnceLock;
fn active_categories() -> &'static HashSet<String> {
static CATEGORIES: OnceLock<HashSet<String>> = OnceLock::new();
CATEGORIES.get_or_init(|| {
let mut set = HashSet::new();
if let Ok(v) = std::env::var("SUPERMACHINE_TRACE") {
let v = v.trim();
if v == "1"
|| v == "all"
|| v == "*"
|| v.eq_ignore_ascii_case("true")
{
set.insert("all".to_owned());
} else {
for tok in v.split(',') {
let t = tok.trim().to_lowercase();
if !t.is_empty() {
set.insert(t);
}
}
}
}
const LEGACY: &[(&str, &str)] = &[
("SUPERMACHINE_BAKE_TRACE", "bake"),
("SUPERMACHINE_VFS_TRACE", "vfs"),
("SUPERMACHINE_BALLOON_TRACE", "balloon"),
("SUPERMACHINE_FUSE_TRACE", "fuse"),
("SUPERMACHINE_TLS_TRACE", "tls"),
("SUPERMACHINE_VQ_TRACE", "vq"),
("SUPERMACHINE_VSOCK_TRACE", "vsock"),
("SUPERMACHINE_ROUTER_TRACE", "router"),
("SUPERMACHINE_REMAP_TIMINGS", "remap"),
("SUPERMACHINE_ROSETTA_TRACE", "rosetta"),
("SUPERMACHINE_TIMEOUT_TRACE", "timeout"),
("SUPERMACHINE_TIMINGS", "timings"),
("SUPERMACHINE_RUN_TRACE", "run"),
];
for (env, cat) in LEGACY {
if std::env::var_os(env).is_some() {
set.insert((*cat).to_owned());
}
}
set
})
}
pub fn enabled(category: &str) -> bool {
let set = active_categories();
if set.contains("all") {
return true;
}
set.contains(category)
}
pub fn fuse_target() -> Option<std::ffi::OsString> {
if let Some(v) = std::env::var_os("SUPERMACHINE_FUSE_TRACE") {
return Some(v);
}
if enabled("fuse") {
Some(std::ffi::OsString::from("/tmp/supermachine-fuse-trace.log"))
} else {
None
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Mutex;
static LOCK: Mutex<()> = Mutex::new(());
fn parse(unified: Option<&str>, legacy: &[(&str, &str)]) -> HashSet<String> {
let mut set = HashSet::new();
if let Some(v) = unified {
let v = v.trim();
if v == "1" || v == "all" || v == "*" || v.eq_ignore_ascii_case("true") {
set.insert("all".to_owned());
} else {
for tok in v.split(',') {
let t = tok.trim().to_lowercase();
if !t.is_empty() {
set.insert(t);
}
}
}
}
for (env_set, cat) in legacy {
if !env_set.is_empty() {
set.insert((*cat).to_owned());
}
}
set
}
#[test]
fn unified_all_matches_every_category() {
let _g = LOCK.lock().unwrap();
let s = parse(Some("all"), &[]);
assert!(s.contains("all"));
}
#[test]
fn unified_star_is_synonym_for_all() {
let _g = LOCK.lock().unwrap();
let s = parse(Some("*"), &[]);
assert!(s.contains("all"));
}
#[test]
fn unified_one_truthy_int_is_synonym_for_all() {
let _g = LOCK.lock().unwrap();
let s = parse(Some("1"), &[]);
assert!(s.contains("all"));
}
#[test]
fn unified_categories_csv_parses() {
let _g = LOCK.lock().unwrap();
let s = parse(Some("bake,vfs, balloon"), &[]);
assert!(s.contains("bake"));
assert!(s.contains("vfs"));
assert!(s.contains("balloon"));
assert!(!s.contains("vsock"));
}
#[test]
fn unified_uppercase_is_normalized() {
let _g = LOCK.lock().unwrap();
let s = parse(Some("BAKE,Vfs"), &[]);
assert!(s.contains("bake"));
assert!(s.contains("vfs"));
}
#[test]
fn legacy_vars_set_their_category() {
let _g = LOCK.lock().unwrap();
let s = parse(None, &[("1", "bake"), ("1", "timings")]);
assert!(s.contains("bake"));
assert!(s.contains("timings"));
assert!(!s.contains("vfs"));
}
#[test]
fn unified_and_legacy_compose() {
let _g = LOCK.lock().unwrap();
let s = parse(Some("vfs"), &[("1", "bake")]);
assert!(s.contains("vfs"));
assert!(s.contains("bake"));
}
#[test]
fn empty_inputs_yield_empty_set() {
let _g = LOCK.lock().unwrap();
let s = parse(None, &[]);
assert!(s.is_empty());
}
#[test]
fn whitespace_only_csv_is_ignored() {
let _g = LOCK.lock().unwrap();
let s = parse(Some(" , ,"), &[]);
assert!(s.is_empty());
}
}