use std::{
fs,
io::{self, Write},
path::Path,
thread,
time::Duration,
};
#[derive(clap::Args, Debug, Clone)]
pub struct BootCli {
#[arg(long)]
pub no_banner: bool,
#[arg(long)]
pub no_anim: bool,
#[arg(long)]
pub no_probes: bool,
}
#[derive(Debug, Clone, Copy)]
pub struct BootConfig {
pub show: bool,
pub anim: bool,
pub probes: bool,
}
impl From<&BootCli> for BootConfig {
fn from(c: &BootCli) -> Self {
Self {
show: !c.no_banner,
anim: !c.no_anim,
probes: !c.no_probes,
}
}
}
pub fn print_boot(cfg: BootConfig) {
if !cfg.show {
return;
}
let name = env!("CARGO_PKG_NAME");
let ver = env!("CARGO_PKG_VERSION");
let sha = option_env!("GIT_SHA").unwrap_or("-");
let built = option_env!("BUILD_TIME_UTC").unwrap_or("-");
let rustc = option_env!("RUSTC_VERSION").unwrap_or("-");
let target = option_env!("TARGET_TRIPLE").unwrap_or("-");
let profile = option_env!("BUILD_PROFILE").unwrap_or("-");
let mut agents = "-".to_string();
let mut vaults = "-".to_string();
let mut reflex = "not found".to_string();
if cfg.probes {
agents = count_by_ext("agents", &["intent", "toml"])
.map(|n| n.to_string())
.unwrap_or("-".into());
vaults = count_by_ext("vaults", &["vault"])
.map(|n| n.to_string())
.unwrap_or("-".into());
reflex = detect_reflex("reflex.toml");
}
let lines = vec![
"╔════════════════════════════════════════════════════════════════╗".into(),
format!("║ VIOS Core OS — FIRST TO CROSS INTACT™ ║"),
"╟────────────────────────────────────────────────────────────────╢".into(),
format!("║ {name} v{ver} git:{sha} built:{built} "),
format!("║ rustc:{rustc} target:{target} profile:{profile} "),
format!(
"║ host:{os}-{arch} pid:{pid:<7} ",
os = std::env::consts::OS,
arch = std::env::consts::ARCH,
pid = std::process::id()
),
"╟────────────────────────────────────────────────────────────────╢".into(),
format!("║ agents:{agents:<6} vaults:{vaults:<6} reflex:{reflex:<16} "),
"╚════════════════════════════════════════════════════════════════╝".into(),
"".into(),
];
if cfg.anim {
type_out(&lines, 6); } else {
for l in lines {
println!("{l}");
}
}
}
fn count_by_ext(dir: &str, exts: &[&str]) -> io::Result<usize> {
let mut n = 0usize;
for e in fs::read_dir(dir)? {
let p = e?.path();
if let Some(ext) = p.extension().and_then(|s| s.to_str()) {
if exts.iter().any(|x| *x == ext) {
n += 1;
}
}
}
Ok(n)
}
fn detect_reflex(path: &str) -> String {
let p = Path::new(path);
if !p.exists() {
return "not found".into();
}
let Ok(content) = fs::read_to_string(p) else {
return "found".into();
};
if let Ok(v) = content.parse::<toml::Value>() {
if let Some(mode) = v.get("mode").and_then(|m| m.as_str()) {
return format!("mode:{mode}");
}
if let Some(tbl) = v.as_table() {
if let Some((k, _)) = tbl.iter().next() {
return format!("toml:{k}");
}
}
"parsed".into()
} else {
"found".into()
}
}
fn type_out(lines: &[String], per_char_ms: u64) {
let mut out = io::stdout();
for line in lines {
for ch in line.chars() {
let _ = write!(out, "{ch}");
let _ = out.flush();
thread::sleep(Duration::from_millis(per_char_ms));
}
let _ = writeln!(out);
}
}