use console::style;
use indicatif::{ProgressBar, ProgressStyle};
use std::time::Duration;
pub fn print_banner() {
let border = style("═").color256(166).to_string(); let b = border.repeat(50);
eprintln!();
eprintln!(" {}", style(&b).color256(166));
eprintln!(
" {} {} {}",
style("║").color256(166),
style(format!(
" ⚡ V A N T A D B {} ⚡ ║",
crate::metadata::version_label()
))
.bold()
.color256(166),
style(" ║").color256(166),
);
eprintln!(
" {} {} {}",
style(" ║").color256(166),
style(" Embedded Persistent Memory Engine ")
.dim()
.white(),
style(" ║").color256(166),
);
eprintln!(
" {} {} {}",
style(" ║").color256(166),
style(" Vector Retrieval · Structured Fields ")
.dim()
.white(),
style(" ║").color256(166),
);
eprintln!(" {}", style(&b).color256(166));
eprintln!();
}
pub fn init_logging(format: crate::config::LogFormat) {
use tracing_subscriber::fmt;
use tracing_subscriber::EnvFilter;
let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
match format {
crate::config::LogFormat::Json => {
fmt::Subscriber::builder()
.with_env_filter(filter)
.with_target(true)
.with_thread_ids(true)
.with_file(true)
.with_line_number(true)
.with_ansi(false)
.json()
.init();
}
crate::config::LogFormat::Full => {
fmt::Subscriber::builder()
.with_env_filter(filter)
.with_target(true)
.with_thread_ids(true)
.with_file(true)
.with_line_number(true)
.with_ansi(true)
.init();
}
crate::config::LogFormat::Compact => {
fmt::Subscriber::builder()
.with_env_filter(filter)
.with_target(false)
.with_thread_ids(false)
.with_file(false)
.with_line_number(false)
.with_ansi(true)
.compact()
.init();
}
}
}
pub fn ok(label: &str, detail: Option<&str>) {
let check = style("[✔]").green().bold();
let lbl = style(label).white().bold();
match detail {
Some(d) => eprintln!(" {} {:<36} {}", check, lbl, style(d).dim()),
None => eprintln!(" {} {}", check, lbl),
}
}
pub fn progress(label: &str, detail: Option<&str>) {
let arrow = style("[→]").cyan().bold();
let lbl = style(label).white();
match detail {
Some(d) => eprintln!(" {} {:<36} {}", arrow, lbl, style(d).dim()),
None => eprintln!(" {} {}", arrow, lbl),
}
}
pub fn warn(label: &str, detail: Option<&str>) {
let ico = style("[⚠]").yellow().bold();
let lbl = style(label).yellow();
match detail {
Some(d) => eprintln!(" {} {:<36} {}", ico, lbl, style(d).dim()),
None => eprintln!(" {} {}", ico, lbl),
}
}
pub fn error(label: &str, detail: Option<&str>) {
let ico = style("[✗]").red().bold();
let lbl = style(label).red().bold();
match detail {
Some(d) => eprintln!(" {} {:<36} {}", ico, lbl, style(d).dim()),
None => eprintln!(" {} {}", ico, lbl),
}
}
pub fn section(title: &str) {
let line = style("─").color256(166).to_string().repeat(48);
eprintln!();
eprintln!(
" {} {} {}",
style("┤").color256(166),
style(title).color256(166).bold(),
style("├").color256(166),
);
eprintln!(" {}", style(&line).color256(166).dim());
}
pub fn separator() {
eprintln!(" {}", style("─".repeat(50)).color256(166).dim());
}
pub fn print_startup_summary(
profile: &str,
instructions: &str,
total_memory_mb: u64,
rocksdb_budget_mb: u64,
backend_mode: &str,
data_dir: &str,
) {
section("System Configuration");
eprintln!(
" {} {:<20} {}",
style("│").color256(166).dim(),
style("Hardware:").dim(),
style(profile).bold().white(),
);
eprintln!(
" {} {:<20} {}",
style("│").color256(166).dim(),
style("Instructions:").dim(),
style(instructions).bold().white(),
);
eprintln!(
" {} {:<20} {}",
style("│").color256(166).dim(),
style("Total Memory:").dim(),
style(format!("{} MB", total_memory_mb)).bold().white(),
);
eprintln!(
" {} {:<20} {}",
style("│").color256(166).dim(),
style("RocksDB Budget:").dim(),
style(format!("{} MB", rocksdb_budget_mb))
.color256(166)
.bold(),
);
eprintln!(
" {} {:<20} {}",
style("│").color256(166).dim(),
style("HNSW Backend:").dim(),
style(backend_mode).bold().white(),
);
eprintln!(
" {} {:<20} {}",
style("│").color256(166).dim(),
style("Data Dir:").dim(),
style(data_dir).dim().white(),
);
separator();
eprintln!();
}
pub fn print_ready(addr: &str) {
eprintln!();
eprintln!(
" {} {} {}",
style("[→]").color256(166).bold(),
style("Listening on").white(),
style(addr).color256(166).bold().underlined(),
);
eprintln!(
" {} {}",
style(" ").dim(),
style("VantaDB is ready for connections.").dim(),
);
eprintln!();
}
pub fn create_progress_bar(total: u64, message: &str) -> ProgressBar {
let pb = ProgressBar::new(total);
pb.set_style(
ProgressStyle::with_template(
" {spinner:.color256(166)} [{bar:40.color256(166)/dim}] {pos}/{len} {msg}",
)
.unwrap_or_else(|_| ProgressStyle::default_bar())
.progress_chars("█▉▊▋▌▍▎▏ ")
.tick_strings(&["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]),
);
pb.set_message(message.to_string());
pb.enable_steady_tick(Duration::from_millis(80));
pb
}
pub fn create_spinner(message: &str) -> ProgressBar {
let pb = ProgressBar::new_spinner();
pb.set_style(
ProgressStyle::with_template(" {spinner:.color256(166)} {msg}")
.unwrap_or_else(|_| ProgressStyle::default_spinner())
.tick_strings(&["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]),
);
pb.set_message(message.to_string());
pb.enable_steady_tick(Duration::from_millis(80));
pb
}
pub fn format_bytes(bytes: u64) -> String {
const KB: u64 = 1_024;
const MB: u64 = 1_024 * KB;
const GB: u64 = 1_024 * MB;
match bytes {
b if b >= GB => format!("{:.1} GB", b as f64 / GB as f64),
b if b >= MB => format!("{:.1} MB", b as f64 / MB as f64),
b if b >= KB => format!("{:.1} KB", b as f64 / KB as f64),
b => format!("{} B", b),
}
}
pub fn format_duration_ms(ms: u128) -> String {
match ms {
t if t < 1 => format!("{}µs", t * 1000),
t if t < 1_000 => format!("{}ms", t),
t => format!("{:.2}s", t as f64 / 1000.0),
}
}