use clap::Parser;
use std::num::NonZero;
use std::path::PathBuf;
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
use tokio::signal;
#[derive(Parser)]
#[command(name = "vtcode-file-search")]
#[command(about = "Fast fuzzy file search for VT Code")]
#[command(version)]
struct Cli {
pattern: Option<String>,
#[arg(short, long, default_value = ".")]
cwd: PathBuf,
#[arg(short, long, default_value = "100")]
limit: NonZero<usize>,
#[arg(short, long)]
exclude: Vec<String>,
#[arg(short, long)]
threads: Option<NonZero<usize>>,
#[arg(long)]
json: bool,
#[arg(long)]
compute_indices: bool,
#[arg(long, default_value = "true")]
respect_gitignore: bool,
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let cli = Cli::parse();
let pattern = match cli.pattern {
Some(pattern) => pattern,
None => {
eprintln!("Missing required search pattern");
std::process::exit(1);
}
};
let threads = match cli.threads {
Some(threads) => threads,
None => NonZero::new(num_cpus::get()).ok_or_else(|| {
anyhow::anyhow!("num_cpus::get() returned 0 while resolving worker thread count")
})?,
};
let cancel_flag = Arc::new(AtomicBool::new(false));
let cancel_flag_clone = cancel_flag.clone();
tokio::spawn(async move {
let _ = signal::ctrl_c().await;
cancel_flag_clone.store(true, std::sync::atomic::Ordering::Relaxed);
});
let results = vtcode_file_search::run(vtcode_file_search::FileSearchConfig {
pattern_text: pattern,
limit: cli.limit,
search_directory: cli.cwd,
exclude: cli.exclude,
threads,
cancel_flag,
compute_indices: cli.compute_indices,
respect_gitignore: cli.respect_gitignore,
})?;
if cli.json {
let json = serde_json::to_string_pretty(&results.matches)?;
println!("{}", json);
} else {
for m in &results.matches {
println!("{} (score: {})", m.path, m.score);
}
if results.total_match_count > cli.limit.get() {}
}
Ok(())
}