mod analytics;
mod cli;
pub mod comment;
mod config;
mod discover;
mod error;
mod log;
mod report;
use clap::Parser;
use crate::cli::{CliArgs, CommandType};
use crate::config::Config;
use crate::discover::discover;
use tracing::{debug, trace};
fn main() {
let args = CliArgs::parse();
log::init().expect("Failed to initialize logging");
let config = match Config::load(args.config.as_deref()) {
Ok(config) => config,
Err(e) => {
eprintln!("Error loading configuration: {}", e);
std::process::exit(1);
}
};
let (command_type, scan_path) = args.get_command_and_path(&config);
match command_type {
CommandType::Scan => {
trace!("Starting nolo TODO comment scan");
run_scan(&scan_path, &config);
}
CommandType::Report => {
trace!("Starting nolo TODO analytics report");
let format = args.get_report_format(&config);
let display_limit = args.get_display_limit(&config);
run_report(&scan_path, format, display_limit, &config);
}
}
}
fn run_scan(scan_path: &std::path::Path, config: &Config) {
let paths = match discover(scan_path) {
Ok(paths) => paths,
Err(e) => {
eprintln!("Error: {}", e);
std::process::exit(1);
}
};
let keywords = config.get_keywords();
for path in paths {
debug!("Reading content from: {:?}", path);
let todo_comments =
comment::extract_todo_comments_with_language_detection(path.clone(), &keywords);
if !todo_comments.is_empty() {
debug!("Found {} TODO comments in {:?}", todo_comments.len(), path);
for todo in todo_comments {
let relative_path = report::get_relative_path(&path, scan_path);
println!("{}:{}: {}", relative_path, todo.line, todo.text);
}
}
}
}
fn run_report(
scan_path: &std::path::Path,
format: crate::cli::ReportFormat,
display_limit: Option<usize>,
config: &Config,
) {
let mut file_todos = Vec::new();
let mut total_files = 0;
let paths = match discover(scan_path) {
Ok(paths) => paths,
Err(e) => {
eprintln!("Error: {}", e);
std::process::exit(1);
}
};
let keywords = config.get_keywords();
for path in paths {
debug!("Reading content from: {:?}", path);
total_files += 1;
let todo_comments =
comment::extract_todo_comments_with_language_detection(path.clone(), &keywords);
file_todos.push((path, todo_comments));
}
let analytics = analytics::analyze_todos(
file_todos,
total_files,
scan_path.to_path_buf(),
display_limit,
&keywords,
);
match format {
crate::cli::ReportFormat::Table => {
report::print_table_report(&analytics);
}
crate::cli::ReportFormat::Json => {
print_json_report(&analytics);
}
}
}
fn print_json_report(analytics: &analytics::TodoAnalytics) {
match serde_json::to_string_pretty(analytics) {
Ok(json) => println!("{}", json),
Err(e) => {
eprintln!("Error serializing to JSON: {}", e);
std::process::exit(1);
}
}
}