use std::path::{Path, PathBuf}; use clap::{Parser, Subcommand}; use colored::*; use git2::Repository; /// Repotool is a tool to manage multiple code repositories #[derive(Parser, Debug)] #[command(version, about)] struct Args { #[command(subcommand)] command: Command, } #[derive(Subcommand, Debug)] enum Command { ///List all repositories found in the directory tree List { /// Directory to start from #[arg(short, long)] directory: PathBuf, }, } fn main() -> Result<(), std::io::Error> { let args = Args::parse(); match args.command { Command::List { directory } => list_repos(directory)?, } Ok(()) } fn list_repos(directory: PathBuf) -> Result<(), std::io::Error> { use std::fs; let start = fs::canonicalize(directory)?; println!( "Listing repositories under: {}", start.display().to_string().yellow() ); println!(); fn gather_repos(dir: &Path, recurse_level: usize) -> Result, std::io::Error> { let mut repos = Vec::new(); let dir = fs::read_dir(dir)?; for entry in dir { let entry = entry?; let path = entry.path(); let hidden = path .file_name() .map(|name| name.as_encoded_bytes()) .and_then(|bytes| bytes.first()) .map(|byte| *byte == b'.') .unwrap_or(false); if let Ok(_repo) = Repository::open(&path) { repos.push(path); } else if path.is_dir() && !hidden { repos.extend(gather_repos(&path, recurse_level + 1)?.into_iter()); } } Ok(repos) } let repo_paths = gather_repos(&start, 0)?; for path in &repo_paths { println!("Repository: {}", path.display().to_string().yellow()); /* let indent = recurse_level * INDENT_INCREMENT; print!("{: <1$}", "", indent); */ } println!(); println!("Total repos: {}", repo_paths.len()); Ok(()) }