use std::time::Instant;
use anyhow::Result;
use clap::ArgGroup;
use clap::Parser;
#[cfg(not(target_env = "msvc"))]
#[global_allocator]
static GLOBAL: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
#[cfg(target_env = "msvc")]
#[global_allocator]
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
use llmcc::LlmccOptions;
use llmcc::{LangProcessorRegistry, run_main, run_main_auto};
use llmcc_dot::ComponentDepth;
use llmcc_rust::LangRust;
use llmcc_ts::LangTypeScript;
fn build_language_registry() -> LangProcessorRegistry {
let mut registry = LangProcessorRegistry::new();
registry.register::<LangRust>("rust");
registry.register::<LangTypeScript>("typescript");
registry
}
#[derive(Parser, Debug)]
#[command(
name = "llmcc",
about = "llmcc: zoom in, zoom out, understand everything",
version,
group = ArgGroup::new("inputs").required(true).args(["files", "dirs"])
)]
pub struct Cli {
#[arg(
short = 'f',
long = "file",
value_name = "FILE",
num_args = 1..,
action = clap::ArgAction::Append,
conflicts_with = "dirs"
)]
files: Vec<String>,
#[arg(
short = 'd',
long = "dir",
value_name = "DIR",
num_args = 1..,
action = clap::ArgAction::Append,
conflicts_with = "files"
)]
dirs: Vec<String>,
#[arg(long, value_name = "LANG", default_value = "auto")]
lang: String,
#[arg(long, default_value_t = false)]
print_ir: bool,
#[arg(long, default_value_t = false)]
print_block: bool,
#[arg(long, default_value_t = false)]
graph: bool,
#[arg(long = "depth", default_value = "3")]
component_depth: usize,
#[arg(long = "pagerank-top-k")]
pagerank_top_k: Option<usize>,
#[arg(long = "cluster-by-crate")]
cluster_by_crate: bool,
#[arg(long = "short-labels")]
short_labels: bool,
#[arg(short = 'o', long = "output", value_name = "FILE")]
output: Option<String>,
}
pub fn run(args: Cli) -> Result<()> {
let total_start = Instant::now();
if std::env::var("RUST_LOG").is_ok() {
tracing_subscriber::fmt()
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
.with_writer(std::io::stderr)
.init();
}
let opts = LlmccOptions {
files: args.files,
dirs: args.dirs,
output: args.output.clone(),
print_ir: args.print_ir,
print_block: args.print_block,
graph: args.graph,
component_depth: ComponentDepth::from_number(args.component_depth),
pagerank_top_k: args.pagerank_top_k,
cluster_by_crate: args.cluster_by_crate,
short_labels: args.short_labels,
};
let result = match args.lang.as_str() {
"auto" => {
let registry = build_language_registry();
run_main_auto(&opts, ®istry)
}
"rust" => run_main::<LangRust>(&opts),
"typescript" | "ts" => run_main::<LangTypeScript>(&opts),
_ => Err(format!(
"Unknown language: {}. Use 'auto', 'rust', or 'typescript'",
args.lang
)
.into()),
};
match result {
Ok(Some(output)) => {
if let Some(ref path) = args.output {
std::fs::write(path, &output)?;
tracing::info!("output written to: {}", path);
} else {
println!("{output}");
}
}
Ok(None) => {
}
Err(e) => {
eprintln!("Error: {e}");
tracing::error!("Error: {}", e);
}
}
let total_secs = total_start.elapsed().as_secs_f64();
tracing::info!("Total time: {:.2}s", total_secs);
eprintln!("Total time: {total_secs:.2}s");
Ok(())
}
pub fn main() -> Result<()> {
let args = Cli::parse();
run(args)
}