vyctor 0.1.0

A fast CLI tool for semantic file search using vector embeddings
Documentation
//! Implementation of the `vyctor status` command

use crate::config::{db_path, find_vyctor_root, load_config};
use crate::daemon::is_daemon_running;
use crate::storage::Storage;
use anyhow::Result;
use colored::Colorize;

/// Run the status command
pub async fn run() -> Result<()> {
    let root = find_vyctor_root()?;
    let config = load_config()?;

    println!("{}", "Vyctor Status".bold());
    println!();

    // Show root directory
    println!("{}", "Location:".bold());
    println!("  Root: {}", root.display());
    println!("  Config: {}", root.join("vyctor.config.toml").display());
    println!();

    // Show configuration
    println!("{}", "Configuration:".bold());
    println!(
        "  Provider: {}",
        format!("{:?}", config.embedding.provider).cyan()
    );
    println!("  Model: {}", config.embedding.get_model().cyan());
    println!(
        "  Dimensions: {}",
        config.embedding.dimensions.to_string().cyan()
    );
    println!(
        "  Chunk size: {} chars",
        config.indexing.chunk_size.to_string().cyan()
    );
    println!(
        "  Chunk overlap: {} chars",
        config.indexing.chunk_overlap.to_string().cyan()
    );
    println!();

    // Try to get database stats (read-only to allow concurrent access with daemon)
    let db_file = db_path()?;
    if db_file.exists() {
        match Storage::open_readonly(&db_file, config.embedding.dimensions) {
            Ok(storage) => {
                match storage.get_stats() {
                    Ok(stats) => {
                        println!("{}", "Index Statistics:".bold());
                        println!("  Files indexed: {}", stats.file_count.to_string().green());
                        println!("  Total chunks: {}", stats.chunk_count.to_string().cyan());
                        println!(
                            "  Content size: {}",
                            format_bytes(stats.total_content_size).cyan()
                        );
                        if let Some(last) = stats.last_indexed {
                            println!("  Last indexed: {}", last.dimmed());
                        }

                        // Show database file size
                        if let Ok(metadata) = std::fs::metadata(&db_file) {
                            println!(
                                "  Database size: {}",
                                format_bytes(metadata.len() as usize).cyan()
                            );
                        }
                    }
                    Err(e) => {
                        println!("{} Could not read index stats: {}", "!".yellow(), e);
                    }
                }
            }
            Err(_e) => {
                // Check if daemon is holding the lock
                if let Some(pid) = is_daemon_running(&root) {
                    println!("{}", "Index Statistics:".bold());
                    println!(
                        "  {} Database locked by watcher daemon (PID {})",
                        "!".yellow(),
                        pid
                    );
                    println!("  Stop daemon to view detailed stats: vyctor watch --stop");
                } else {
                    println!("{} Could not open database", "!".yellow());
                    println!("  The VSS extension may not be installed.");
                }
            }
        }
    } else {
        println!("{}", "Index:".bold());
        println!(
            "  {} No index found. Run 'vyctor sync' to create one.",
            "!".yellow()
        );
    }

    println!();

    // Show include/exclude patterns
    println!("{}", "File Patterns:".bold());
    println!("  Include:");
    for pattern in config.indexing.include.iter().take(5) {
        println!("{}", pattern);
    }
    if config.indexing.include.len() > 5 {
        println!("    ... and {} more", config.indexing.include.len() - 5);
    }

    println!("  Exclude:");
    for pattern in config.indexing.exclude.iter().take(5) {
        println!("{}", pattern);
    }
    if config.indexing.exclude.len() > 5 {
        println!("    ... and {} more", config.indexing.exclude.len() - 5);
    }

    Ok(())
}

/// Format bytes into human-readable form
fn format_bytes(bytes: usize) -> String {
    const KB: usize = 1024;
    const MB: usize = KB * 1024;
    const GB: usize = MB * 1024;

    if bytes >= GB {
        format!("{:.2} GB", bytes as f64 / GB as f64)
    } else if bytes >= MB {
        format!("{:.2} MB", bytes as f64 / MB as f64)
    } else if bytes >= KB {
        format!("{:.2} KB", bytes as f64 / KB as f64)
    } else {
        format!("{} bytes", bytes)
    }
}