use anyhow::{Context, Result};
use std::path::PathBuf;
use crate::cache::CacheManager;
use crate::indexer::Indexer;
use crate::models::{IndexConfig, Language};
pub(super) fn handle_index_status() -> Result<()> {
log::info!("Checking background symbol indexing status");
let cache = CacheManager::new(".");
let cache_path = cache.path().to_path_buf();
match crate::background_indexer::BackgroundIndexer::get_status(&cache_path) {
Ok(Some(status)) => {
println!("Background Symbol Indexing Status");
println!("==================================");
println!("State: {:?}", status.state);
println!("Total files: {}", status.total_files);
println!("Processed: {}", status.processed_files);
println!("Cached: {}", status.cached_files);
println!("Parsed: {}", status.parsed_files);
println!("Failed: {}", status.failed_files);
println!("Started: {}", status.started_at);
println!("Last updated: {}", status.updated_at);
if let Some(completed_at) = &status.completed_at {
println!("Completed: {}", completed_at);
}
if let Some(error) = &status.error {
println!("Error: {}", error);
}
if status.state == crate::background_indexer::IndexerState::Running && status.total_files > 0 {
let progress = (status.processed_files as f64 / status.total_files as f64) * 100.0;
println!("\nProgress: {:.1}%", progress);
}
Ok(())
}
Ok(None) => {
println!("No background symbol indexing in progress.");
println!("\nRun 'rfx index' to start background symbol indexing.");
Ok(())
}
Err(e) => {
anyhow::bail!("Failed to get indexing status: {}", e);
}
}
}
pub(super) fn handle_index_compact(json: &bool, pretty: &bool) -> Result<()> {
log::info!("Running cache compaction");
let cache = CacheManager::new(".");
let report = cache.compact()?;
if *json {
let json_str = if *pretty {
serde_json::to_string_pretty(&report)?
} else {
serde_json::to_string(&report)?
};
println!("{}", json_str);
} else {
println!("Cache Compaction Complete");
println!("=========================");
println!("Files removed: {}", report.files_removed);
println!("Space saved: {:.2} MB", report.space_saved_bytes as f64 / 1_048_576.0);
println!("Duration: {}ms", report.duration_ms);
}
Ok(())
}
pub(super) fn handle_index_build(path: &PathBuf, force: &bool, languages: &[String], quiet: &bool) -> Result<()> {
log::info!("Starting index build");
let cache = CacheManager::new(path);
let cache_path = cache.path().to_path_buf();
if *force {
log::info!("Force rebuild requested, clearing existing cache");
cache.clear()?;
}
let lang_filters: Vec<Language> = languages
.iter()
.filter_map(|s| {
Language::from_name(s).or_else(|| {
eprintln!("Warning: Unknown language: '{}'. Supported: {}", s, Language::supported_names_help());
None
})
})
.collect();
let config = IndexConfig {
languages: lang_filters,
..Default::default()
};
let indexer = Indexer::new(cache, config);
let show_progress = !quiet;
let stats = indexer.index(path, show_progress)?;
if !quiet {
println!("Indexing complete!");
println!(" Files indexed: {}", stats.total_files);
println!(" Cache size: {}", format_bytes(stats.index_size_bytes));
println!(" Last updated: {}", stats.last_updated);
if !stats.files_by_language.is_empty() {
println!("\nFiles by language:");
let mut lang_vec: Vec<_> = stats.files_by_language.iter().collect();
lang_vec.sort_by(|a, b| b.1.cmp(a.1).then(a.0.cmp(b.0)));
let max_lang_len = lang_vec.iter().map(|(lang, _)| lang.len()).max().unwrap_or(8);
let lang_width = max_lang_len.max(8);
println!(" {:<width$} Files Lines", "Language", width = lang_width);
println!(" {} ----- -------", "-".repeat(lang_width));
for (language, file_count) in lang_vec {
let line_count = stats.lines_by_language.get(language).copied().unwrap_or(0);
println!(" {:<width$} {:5} {:7}",
language, file_count, line_count,
width = lang_width);
}
}
}
if !crate::background_indexer::BackgroundIndexer::is_running(&cache_path) {
if !quiet {
println!("\nStarting background symbol indexing...");
println!(" Symbols will be cached for faster queries");
println!(" Check status with: rfx index status");
}
let current_exe = std::env::current_exe()
.context("Failed to get current executable path")?;
#[cfg(unix)]
{
std::process::Command::new(¤t_exe)
.arg("index-symbols-internal")
.arg(path)
.stdin(std::process::Stdio::null())
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.spawn()
.context("Failed to spawn background indexing process")?;
}
#[cfg(windows)]
{
use std::os::windows::process::CommandExt;
const CREATE_NO_WINDOW: u32 = 0x08000000;
std::process::Command::new(¤t_exe)
.arg("index-symbols-internal")
.arg(&path)
.creation_flags(CREATE_NO_WINDOW)
.stdin(std::process::Stdio::null())
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.spawn()
.context("Failed to spawn background indexing process")?;
}
log::debug!("Spawned background symbol indexing process");
} else if !quiet {
println!("\n⚠️ Background symbol indexing already in progress");
println!(" Check status with: rfx index status");
}
Ok(())
}
fn format_bytes(bytes: u64) -> String {
const KB: u64 = 1024;
const MB: u64 = KB * 1024;
const GB: u64 = MB * 1024;
const TB: u64 = GB * 1024;
if bytes >= TB {
format!("{:.2} TB", bytes as f64 / TB as f64)
} else 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)
}
}
pub(super) fn handle_index_symbols_internal(cache_dir: PathBuf) -> Result<()> {
let mut indexer = crate::background_indexer::BackgroundIndexer::new(&cache_dir)?;
indexer.run()?;
Ok(())
}