use crate::cli::colors as c;
use crate::cli::commands::{DiagnosticOutputFormat, StorageCommand, TdgCommand};
use crate::tdg::{StorageBackendType, StorageConfig, TieredStorageFactory, TieredStore};
use anyhow::Result;
use serde_json::json;
use std::path::PathBuf;
#[cfg(feature = "http-server")]
fn open_browser(url: &str) -> std::io::Result<()> {
#[cfg(target_os = "macos")]
{
std::process::Command::new("open").arg(url).spawn()?;
}
#[cfg(target_os = "linux")]
{
std::process::Command::new("xdg-open").arg(url).spawn()?;
}
#[cfg(target_os = "windows")]
{
std::process::Command::new("cmd")
.args(["/c", "start", url])
.spawn()?;
}
Ok(())
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub async fn handle_tdg_diagnostics(command: &TdgCommand, base_path: &PathBuf) -> Result<()> {
match command {
TdgCommand::Diagnostics {
detailed,
storage,
scheduler,
adaptive,
resources,
all,
format,
} => {
show_diagnostics(
base_path,
*detailed,
*storage || *all,
*scheduler || *all,
*adaptive || *all,
*resources || *all,
format.clone(),
)
.await
}
TdgCommand::Storage { command } => handle_storage_command(command, base_path).await,
TdgCommand::Compare { .. }
| TdgCommand::History { .. }
| TdgCommand::Baseline { .. }
| TdgCommand::CheckRegression { .. }
| TdgCommand::CheckQuality { .. } => {
Ok(())
}
#[cfg(feature = "http-server")]
TdgCommand::Dashboard {
port,
host,
open,
update_interval,
} => handle_dashboard_command(*port, host.clone(), *open, *update_interval).await,
#[cfg(not(feature = "http-server"))]
TdgCommand::Dashboard { .. } => {
anyhow::bail!("Dashboard requires the 'http-server' feature. Rebuild with: cargo build --features http-server")
}
TdgCommand::Config(config_cmd) => {
super::config_command_handlers::handle_config_command(config_cmd).await
}
}
}
async fn show_diagnostics(
base_path: &PathBuf,
detailed: bool,
show_storage: bool,
_show_scheduler: bool,
_show_adaptive: bool,
_show_resources: bool,
format: DiagnosticOutputFormat,
) -> Result<()> {
if !show_storage {
return Ok(());
}
let storage = TieredStorageFactory::create_at_path(base_path)?;
let stats = storage.get_statistics();
match format {
DiagnosticOutputFormat::Plain => show_plain_diagnostics(&stats, detailed),
DiagnosticOutputFormat::Human => show_human_diagnostics(&stats, detailed),
DiagnosticOutputFormat::Json => show_json_diagnostics(&stats, show_storage)?,
DiagnosticOutputFormat::Yaml => show_yaml_diagnostics(&stats, show_storage)?,
DiagnosticOutputFormat::Table => show_table_diagnostics(&stats),
}
Ok(())
}
fn show_plain_diagnostics(stats: &crate::tdg::storage::StorageStatistics, detailed: bool) {
println!("{}\n", c::header("TDG System Diagnostics"));
print_basic_storage_info(stats);
if detailed {
show_backend_details(&stats.backend_stats);
}
}
fn show_human_diagnostics(stats: &crate::tdg::storage::StorageStatistics, detailed: bool) {
println!("{}\n", c::header("TDG System Diagnostics"));
println!("{}", c::subheader("Storage Diagnostics:"));
println!("{}", stats.format_diagnostic());
if detailed {
show_human_backend_details(&stats.backend_stats);
}
println!();
println!(
"{}",
c::dim("Note: Full diagnostic infrastructure is in development.")
);
println!("{}", c::dim("Currently showing storage statistics only."));
}
fn show_json_diagnostics(
stats: &crate::tdg::storage::StorageStatistics,
show_storage: bool,
) -> Result<()> {
let json_output = json!({
"storage": if show_storage { Some(stats) } else { None },
"note": "Full diagnostic infrastructure in development"
});
println!("{}", serde_json::to_string_pretty(&json_output)?);
Ok(())
}
fn show_yaml_diagnostics(
stats: &crate::tdg::storage::StorageStatistics,
show_storage: bool,
) -> Result<()> {
let yaml_output = json!({
"storage": if show_storage { Some(stats) } else { None },
"note": "Full diagnostic infrastructure in development"
});
println!("{}", serde_yaml_ng::to_string(&yaml_output)?);
Ok(())
}
fn show_table_diagnostics(stats: &crate::tdg::storage::StorageStatistics) {
println!("┌─────────────┬─────────────────────┬─────────────────────────────────┐");
println!("│ Component │ Status │ Details │");
println!("├─────────────┼─────────────────────┼─────────────────────────────────┤");
println!(
"│ Storage │ {:>17} │ Hot: {}, Warm: {}, Cold: {} │",
format!("{} entries", stats.total_entries),
stats.hot_entries,
stats.warm_entries,
stats.cold_entries
);
println!(
"│ Backends │ Warm: {:>12} │ Cold: {:>24} │",
stats.warm_backend, stats.cold_backend
);
println!(
"│ Compression │ {:>16.1}% │ Memory: {:>21} KB │",
stats.compression_ratio * 100.0,
stats.hot_memory_kb
);
println!("└─────────────┴─────────────────────┴─────────────────────────────────┘");
}
fn print_basic_storage_info(stats: &crate::tdg::storage::StorageStatistics) {
println!(
"{} {} entries",
c::label("Storage:"),
c::number(&format!("{}", stats.total_entries))
);
println!(
"{} {}, {} {}, {} {}",
c::label("Hot:"),
c::number(&format!("{}", stats.hot_entries)),
c::label("Warm:"),
c::number(&format!("{}", stats.warm_entries)),
c::label("Cold:"),
c::number(&format!("{}", stats.cold_entries))
);
println!(
"{} Warm: {}, Cold: {}",
c::label("Backends -"),
stats.warm_backend,
stats.cold_backend
);
println!(
"{} {}, {} {} KB",
c::label("Compression:"),
c::number(&format!("{:.1}%", stats.compression_ratio * 100.0)),
c::label("Memory:"),
c::number(&format!("{}", stats.hot_memory_kb))
);
}
fn show_backend_details(
backend_stats: &std::collections::HashMap<String, std::collections::HashMap<String, String>>,
) {
println!("\n{}", c::subheader("Backend Details:"));
for (tier, stats) in backend_stats {
println!("{}: {stats:?}", c::label(tier));
}
}
fn show_human_backend_details(
backend_stats: &std::collections::HashMap<String, std::collections::HashMap<String, String>>,
) {
println!("\n{}", c::subheader("Backend Details:"));
for (tier, stats) in backend_stats {
println!(" {}:", c::label(tier));
for (key, value) in stats {
println!(" {}: {}", c::label(key), value);
}
}
}
async fn handle_storage_command(command: &StorageCommand, base_path: &PathBuf) -> Result<()> {
let storage = TieredStorageFactory::create_at_path(base_path)?;
match command {
StorageCommand::Stats { detailed } => handle_stats(&storage, *detailed),
StorageCommand::Cleanup { max_age } => handle_cleanup(&storage, *max_age),
StorageCommand::Migrate { backend, path } => handle_migrate(backend, path.as_ref()),
StorageCommand::Flush => handle_flush(&storage),
}
}
fn handle_stats(storage: &TieredStore, detailed: bool) -> Result<()> {
let stats = storage.get_statistics();
println!("{}\n", c::header("TDG Storage Statistics"));
println!("{}", c::rule());
println!();
println!("{}", c::subheader("Storage Tiers:"));
println!(
" {}: {} entries, {} KB",
c::dim("Hot (memory)"),
c::number(&stats.hot_entries.to_string()),
c::number(&stats.hot_memory_kb.to_string())
);
println!(
" {}: {} entries",
c::dim(&format!("Warm ({} backend)", stats.warm_backend)),
c::number(&stats.warm_entries.to_string())
);
println!(
" {}: {} entries",
c::dim(&format!("Cold ({} backend)", stats.cold_backend)),
c::number(&stats.cold_entries.to_string())
);
println!(
" {}: {}",
c::dim("Total"),
c::number(&stats.total_entries.to_string())
);
println!(
" {}: {}",
c::dim("Compression ratio"),
c::pct(f64::from(stats.compression_ratio) * 100.0, 50.0, 20.0)
);
println!();
if detailed {
print_backend_statistics(&stats.backend_stats);
}
Ok(())
}
fn print_backend_statistics(
backend_stats: &std::collections::HashMap<String, std::collections::HashMap<String, String>>,
) {
println!("\n{}", c::subheader("Backend Statistics:"));
for (tier, backend_stats) in backend_stats {
println!("\n{}:", c::label(tier));
for (key, value) in backend_stats {
println!(" {}: {}", c::label(key), value);
}
}
}
fn handle_cleanup(storage: &TieredStore, max_age: u64) -> Result<()> {
let removed = storage.cleanup_hot_cache(max_age);
println!(
"{}",
c::pass(&format!(
"Cleaned up {} expired hot cache entries",
c::number(&format!("{removed}"))
))
);
Ok(())
}
fn handle_migrate(backend: &str, path: Option<&PathBuf>) -> Result<()> {
let backend_type = parse_backend_type(backend)?;
let (warm_config, cold_config) = create_migration_configs(backend_type, path);
println!(
"{}",
c::dim(&format!(
"Migrating storage to {} backend...",
c::label(backend)
))
);
println!("{}", c::warn("Migration requires restart to take effect"));
println!("{}", c::subheader("New configuration:"));
println!(" {} {warm_config:?}", c::label("Warm storage:"));
println!(" {} {cold_config:?}", c::label("Cold storage:"));
Ok(())
}
fn parse_backend_type(backend: &str) -> Result<StorageBackendType> {
match backend {
"libsql" => Ok(StorageBackendType::Libsql),
"inmemory" => Ok(StorageBackendType::InMemory),
_ => Err(anyhow::anyhow!(
"Unknown backend type: {backend}. Valid options: libsql, inmemory"
)),
}
}
fn create_migration_configs(
backend_type: StorageBackendType,
path: Option<&PathBuf>,
) -> (StorageConfig, StorageConfig) {
let warm_config = StorageConfig {
backend_type,
path: path.map(|p| p.join("tdg-warm")),
cache_size_mb: Some(128),
compression: true,
};
let cold_config = StorageConfig {
backend_type,
path: path.map(|p| p.join("tdg-cold")),
cache_size_mb: Some(64),
compression: false,
};
(warm_config, cold_config)
}
fn handle_flush(storage: &TieredStore) -> Result<()> {
storage.flush()?;
println!("{}", c::pass("All pending writes flushed to storage"));
Ok(())
}
#[cfg(feature = "http-server")]
async fn handle_dashboard_command(
port: u16,
host: String,
open: bool,
_update_interval: u64,
) -> Result<()> {
use crate::tdg::web_dashboard::start_dashboard_server;
use std::net::{IpAddr, SocketAddr};
println!("{}", c::header("Starting TDG Dashboard server..."));
let addr: IpAddr = host.parse()?;
let socket_addr = SocketAddr::new(addr, port);
println!(
"{} {}",
c::label("Dashboard:"),
c::path(&format!("http://{host}:{port}"))
);
println!("{}", c::pass("Real-time metrics updates enabled"));
if open {
if let Err(e) = open_browser(&format!("http://{host}:{port}")) {
eprintln!("{}", c::warn(&format!("Could not open browser: {e}")));
} else {
println!("{}", c::dim("Opening dashboard in browser..."));
}
}
println!("{}", c::dim("Press Ctrl+C to stop the server"));
start_dashboard_server(socket_addr)
.await
.map_err(|e| anyhow::anyhow!("Dashboard server error: {e}"))?;
Ok(())
}
#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod property_tests {
use proptest::prelude::*;
proptest! {
#[test]
fn basic_property_stability(_input in ".*") {
prop_assert!(true);
}
#[test]
fn module_consistency_check(_x in 0u32..1000) {
prop_assert!(_x < 1001);
}
}
}