use owo_colors::OwoColorize;
use owo_colors::Stream;
use baztet_core::exec::ExecResult;
pub fn ok(msg: &str) {
eprintln!(
"{}",
format!("✓ {msg}").if_supports_color(Stream::Stderr, |t| t.green().to_string())
);
}
pub fn warn(msg: &str) {
eprintln!(
"{}",
format!("! {msg}").if_supports_color(Stream::Stderr, |t| t.yellow().to_string())
);
}
pub fn fail(msg: &str) {
eprintln!(
"{}",
format!("✗ {msg}").if_supports_color(Stream::Stderr, |t| t.red().to_string())
);
}
pub fn phase(n: u8, total: u8, label: &str, secs: f64) {
eprintln!(
"{}",
format!("[{n}/{total}] {label} ({secs:.1}s)")
.if_supports_color(Stream::Stderr, |t| t.blue().to_string())
);
}
pub fn test_status_colored(status: &str) -> String {
format!("{status:>8}")
.if_supports_color(Stream::Stdout, |t| match status {
"PASSED" => t.bright_green().to_string(),
"FAILED" => t.bright_red().to_string(),
_ => t.bright_yellow().to_string(),
})
.to_string()
}
pub fn status_is_failure(status: &str) -> bool {
status != "PASSED"
}
pub fn present_exec(command: &str, result: &ExecResult, show_status: bool) {
let Some(out) = &result.output else {
return;
};
let warns = baztet_core::exec::warn_lines(out);
for w in warns.iter().take(30) {
warn(w);
}
if warns.len() > 30 {
warn(&format!(
"... {} more warning lines suppressed",
warns.len() - 30
));
}
if result.code == 0 && show_status {
ok(&format!("{command} ok ({:.1}s)", result.secs));
}
if result.code != 0 {
let stderr_text = String::from_utf8_lossy(&out.stderr);
let stdout_text = String::from_utf8_lossy(&out.stdout);
let combined = if stderr_text.trim().is_empty() {
stdout_text.to_string()
} else {
format!("{stderr_text}\n{stdout_text}")
};
let lines: Vec<&str> = combined.lines().collect();
let tail = &lines[lines.len().saturating_sub(400)..];
for line in tail {
eprintln!("{line}");
}
}
}
pub fn doctor_report(
ws: &camino::Utf8PathBuf,
cli_bazel: Option<&str>,
) -> anyhow::Result<(String, bool)> {
use std::fmt::Write as DocWrite;
let mut report = String::new();
let mut ok = true;
writeln!(report, "baztet doctor")?;
let binary = cli_bazel.unwrap_or("bazel");
let version = std::process::Command::new(binary).arg("--version").output();
match version {
Ok(out) if out.status.success() => {
writeln!(
report,
"bazel: {} ({})",
binary,
String::from_utf8_lossy(&out.stdout).trim()
)?;
}
_ => {
writeln!(report, "bazel: {binary} NOT FOUND or not runnable")?;
ok = false;
}
}
writeln!(report, "workspace: {ws}")?;
match baztet_core::workspace::find_project_view(ws) {
Some(pv_path) => writeln!(report, "project view: {pv_path}")?,
None => writeln!(
report,
"project view: not present (tried {})",
baztet_core::workspace::PV_CANDIDATES.join(", ")
)?,
}
match baztet_snapshot::Snapshot::load(ws) {
Ok(snapshot) => writeln!(
report,
"snapshot: {} target(s), {} ide entries{}, last fingerprint {:?}",
snapshot.graph.len(),
snapshot.ide_infos.len(),
if snapshot.has_error {
" (has errors)"
} else {
""
},
snapshot.sync_fingerprint,
)?,
Err(e) => writeln!(report, "snapshot: none ({e})")?,
}
let aspect = ws.join(baztet_core::sync::ASPECT_PREFIX);
match aspect.join("config/aspect.bzl").is_file() {
true => writeln!(report, "aspect: deployed at {aspect}")?,
false => writeln!(
report,
"aspect: not deployed yet (first sync) and .bazelbsp (IDE) state unknown; not an error"
)?,
}
Ok((report, ok))
}