Print flat or indented
This commit is contained in:
121
src/main.rs
121
src/main.rs
@@ -1,3 +1,4 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use clap::{Parser, Subcommand};
|
||||
@@ -14,7 +15,7 @@ struct Args {
|
||||
|
||||
struct Repo {
|
||||
path: PathBuf,
|
||||
remotes: Vec<(String, String)>
|
||||
remotes: Vec<(String, String)>,
|
||||
}
|
||||
|
||||
#[derive(Subcommand, Debug)]
|
||||
@@ -24,18 +25,22 @@ enum Command {
|
||||
/// Directory to start from
|
||||
#[arg(short, long)]
|
||||
directory: PathBuf,
|
||||
|
||||
/// Print repositories flat rather than indentend
|
||||
#[arg(short, long)]
|
||||
flat: bool,
|
||||
},
|
||||
}
|
||||
|
||||
fn main() -> Result<(), std::io::Error> {
|
||||
let args = Args::parse();
|
||||
match args.command {
|
||||
Command::List { directory } => list_repos(directory)?,
|
||||
Command::List { directory, flat } => list_repos(directory, flat)?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn list_repos(directory: PathBuf) -> Result<(), std::io::Error> {
|
||||
fn list_repos(directory: PathBuf, flat: bool) -> Result<(), std::io::Error> {
|
||||
use std::fs;
|
||||
|
||||
let start = fs::canonicalize(directory)?;
|
||||
@@ -45,7 +50,7 @@ fn list_repos(directory: PathBuf) -> Result<(), std::io::Error> {
|
||||
);
|
||||
println!();
|
||||
|
||||
fn gather_repos(dir: &Path, recurse_level: usize) -> Result<Vec<Repo>, std::io::Error> {
|
||||
fn gather_repos(dir: &Path) -> Result<Vec<Repo>, std::io::Error> {
|
||||
let mut repos = Vec::new();
|
||||
|
||||
let dir = fs::read_dir(dir)?;
|
||||
@@ -62,41 +67,36 @@ fn list_repos(directory: PathBuf) -> Result<(), std::io::Error> {
|
||||
if let Ok(repository) = Repository::open(&path) {
|
||||
let remotes_array = repository.remotes().unwrap();
|
||||
|
||||
let remotes = remotes_array.into_iter().map(|remote_name| {
|
||||
let name = remote_name.unwrap();
|
||||
let remote = repository.find_remote(name).unwrap();
|
||||
let url = remote.url().unwrap();
|
||||
let remotes = remotes_array
|
||||
.into_iter()
|
||||
.map(|remote_name| {
|
||||
let name = remote_name.unwrap();
|
||||
let remote = repository.find_remote(name).unwrap();
|
||||
let url = remote.url().unwrap();
|
||||
|
||||
(name.to_string(), url.to_string())
|
||||
}).collect();
|
||||
(name.to_string(), url.to_string())
|
||||
})
|
||||
.collect();
|
||||
|
||||
let repo = Repo {
|
||||
path,
|
||||
remotes,
|
||||
};
|
||||
let repo = Repo { path, remotes };
|
||||
repos.push(repo);
|
||||
} else if path.is_dir() && !hidden {
|
||||
repos.extend(gather_repos(&path, recurse_level + 1)?.into_iter());
|
||||
repos.extend(gather_repos(&path)?.into_iter());
|
||||
}
|
||||
}
|
||||
Ok(repos)
|
||||
}
|
||||
|
||||
let mut repos = gather_repos(&start, 0)?;
|
||||
repos.sort_unstable_by_key(|repo| repo.path.clone());
|
||||
for repo in &repos {
|
||||
let path = repo.path.strip_prefix(&start).unwrap();
|
||||
print!("Repository: {}", path.display().to_string().yellow());
|
||||
let repos = {
|
||||
let mut r = gather_repos(&start)?;
|
||||
r.sort_unstable_by_key(|repo| repo.path.clone());
|
||||
r
|
||||
};
|
||||
|
||||
print!(" ");
|
||||
for (name, url) in &repo.remotes {
|
||||
print!("[{name} {url}]");
|
||||
}
|
||||
println!();
|
||||
/*
|
||||
let indent = recurse_level * INDENT_INCREMENT;
|
||||
print!("{: <1$}", "", indent);
|
||||
*/
|
||||
if flat {
|
||||
print_flat(&repos, &start);
|
||||
} else {
|
||||
print_indented(&repos, &start);
|
||||
}
|
||||
|
||||
println!();
|
||||
@@ -104,3 +104,66 @@ fn list_repos(directory: PathBuf) -> Result<(), std::io::Error> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_flat(repos: &[Repo], start: &Path) {
|
||||
for repo in repos {
|
||||
let path = repo.path.strip_prefix(start).unwrap();
|
||||
println!("{}", path.display().to_string().yellow());
|
||||
}
|
||||
}
|
||||
|
||||
fn print_indented(repos: &[Repo], start: &Path) {
|
||||
const INDENT_SIZE: usize = 4;
|
||||
|
||||
// Create a nested structure with proper path components
|
||||
let mut path_structure: BTreeMap<Vec<String>, Vec<&Repo>> = BTreeMap::new();
|
||||
|
||||
for repo in repos {
|
||||
let path = repo.path.strip_prefix(start).unwrap();
|
||||
|
||||
let components: Vec<String> = path
|
||||
.parent()
|
||||
.map(|p| {
|
||||
p.components()
|
||||
.map(|c| c.as_os_str().to_string_lossy().to_string())
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_else(Vec::new);
|
||||
|
||||
path_structure.entry(components).or_default().push(repo);
|
||||
}
|
||||
|
||||
// Print hierarchical structure with proper indentation
|
||||
fn print_path(components: &[String], indent_level: usize, indent_size: usize) {
|
||||
if components.is_empty() {
|
||||
println!(".");
|
||||
return;
|
||||
}
|
||||
|
||||
let indent = " ".repeat(indent_level * indent_size);
|
||||
let line = if indent_level == 0 { "" } else { "└──" };
|
||||
println!("{indent}{line}{}", components.last().unwrap().white());
|
||||
}
|
||||
|
||||
for (path_components, dir_repos) in &path_structure {
|
||||
// Print the path with proper indentation
|
||||
print_path(
|
||||
path_components,
|
||||
path_components.len().saturating_sub(1),
|
||||
INDENT_SIZE,
|
||||
);
|
||||
|
||||
// Print repositories with one more level of indentation
|
||||
let repo_indent = " ".repeat(path_components.len() * INDENT_SIZE);
|
||||
for repo in dir_repos {
|
||||
let repo_name = repo.path.file_name().unwrap().to_string_lossy();
|
||||
print!("{}└── {}", repo_indent, repo_name.yellow());
|
||||
|
||||
print!(" ");
|
||||
for (name, url) in &repo.remotes {
|
||||
print!("[{name} {url}]");
|
||||
}
|
||||
println!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user