use crate::processing::file::create_gitignore_matcher;
use clap::Parser;
use ignore::gitignore::Gitignore;
use std::fs;
use std::path::{Path, PathBuf};
#[derive(Parser, Debug)]
#[command(
name = "uncomment",
version = "1.0.5",
about = "Remove comments from code files."
)]
pub struct Cli {
pub paths: Vec<String>,
#[arg(short, long, default_value_t = false)]
pub remove_todo: bool,
#[arg(short = 'f', long, default_value_t = false)]
pub remove_fixme: bool,
#[arg(short = 'd', long, default_value_t = false)]
pub remove_doc: bool,
#[arg(short = 'i', long)]
pub ignore_patterns: Option<Vec<String>>,
#[arg(long = "no-default-ignores", default_value_t = false)]
pub disable_default_ignores: bool,
#[arg(short, long, hide = true)]
pub output_dir: Option<String>,
#[arg(short = 'n', long, default_value_t = false)]
pub dry_run: bool,
#[arg(long = "no-gitignore", default_value_t = false)]
pub no_gitignore: bool,
}
pub fn collect_files(paths: &[PathBuf], respect_gitignore: bool) -> Vec<PathBuf> {
let mut files = Vec::new();
for path in paths {
if path.is_file() {
if path
.file_name()
.map(|name| name != ".gitignore")
.unwrap_or(true)
{
files.push(path.clone());
}
} else if path.is_dir() {
let gitignore = if respect_gitignore {
create_gitignore_matcher(path)
} else {
Gitignore::empty()
};
walk_dir(path, &gitignore, &mut files, respect_gitignore);
}
}
files
}
fn walk_dir(dir: &Path, gitignore: &Gitignore, files: &mut Vec<PathBuf>, respect_gitignore: bool) {
if let Ok(entries) = fs::read_dir(dir) {
for entry in entries.flatten() {
let path = entry.path();
if path
.file_name()
.map(|name| name == ".gitignore")
.unwrap_or(false)
{
continue;
}
if let Ok(relative_path) = path.strip_prefix(dir) {
if !respect_gitignore
|| !gitignore.matched(relative_path, path.is_dir()).is_ignore()
{
if path.is_dir() {
walk_dir(&path, gitignore, files, respect_gitignore);
} else if path.is_file() {
files.push(path);
}
}
}
}
}
}
pub fn parse_args() -> Cli {
Cli::parse()
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs::{self, File};
use tempfile::tempdir;
#[test]
fn test_collect_files_respects_gitignore() {
let dir = tempdir().unwrap();
File::create(dir.path().join("processed.rs")).unwrap();
File::create(dir.path().join("ignored.rs")).unwrap();
fs::write(dir.path().join(".gitignore"), "ignored.rs").unwrap();
let paths = vec![dir.path().to_path_buf()];
let files = collect_files(&paths, true);
assert_eq!(
files.len(),
1,
"Expected 1 file, got {}: {:?}",
files.len(),
files
);
assert!(files[0].ends_with("processed.rs"));
let files = collect_files(&paths, false);
assert_eq!(
files.len(),
2,
"Expected 2 files, got {}: {:?}",
files.len(),
files
);
}
#[test]
fn test_cli_parsing() {
let cli = Cli::parse_from(["uncomment", "src/main.rs", "--remove-todo"]);
assert_eq!(cli.paths, vec!["src/main.rs"]);
assert!(cli.remove_todo);
assert!(!cli.remove_fixme);
assert!(!cli.no_gitignore);
}
}