Print flat or indented
This commit is contained in:
113
src/main.rs
113
src/main.rs
@@ -1,3 +1,4 @@
|
|||||||
|
use std::collections::BTreeMap;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
@@ -14,7 +15,7 @@ struct Args {
|
|||||||
|
|
||||||
struct Repo {
|
struct Repo {
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
remotes: Vec<(String, String)>
|
remotes: Vec<(String, String)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subcommand, Debug)]
|
#[derive(Subcommand, Debug)]
|
||||||
@@ -24,18 +25,22 @@ enum Command {
|
|||||||
/// Directory to start from
|
/// Directory to start from
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
directory: PathBuf,
|
directory: PathBuf,
|
||||||
|
|
||||||
|
/// Print repositories flat rather than indentend
|
||||||
|
#[arg(short, long)]
|
||||||
|
flat: bool,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<(), std::io::Error> {
|
fn main() -> Result<(), std::io::Error> {
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
match args.command {
|
match args.command {
|
||||||
Command::List { directory } => list_repos(directory)?,
|
Command::List { directory, flat } => list_repos(directory, flat)?,
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_repos(directory: PathBuf) -> Result<(), std::io::Error> {
|
fn list_repos(directory: PathBuf, flat: bool) -> Result<(), std::io::Error> {
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
let start = fs::canonicalize(directory)?;
|
let start = fs::canonicalize(directory)?;
|
||||||
@@ -45,7 +50,7 @@ fn list_repos(directory: PathBuf) -> Result<(), std::io::Error> {
|
|||||||
);
|
);
|
||||||
println!();
|
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 mut repos = Vec::new();
|
||||||
|
|
||||||
let dir = fs::read_dir(dir)?;
|
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) {
|
if let Ok(repository) = Repository::open(&path) {
|
||||||
let remotes_array = repository.remotes().unwrap();
|
let remotes_array = repository.remotes().unwrap();
|
||||||
|
|
||||||
let remotes = remotes_array.into_iter().map(|remote_name| {
|
let remotes = remotes_array
|
||||||
|
.into_iter()
|
||||||
|
.map(|remote_name| {
|
||||||
let name = remote_name.unwrap();
|
let name = remote_name.unwrap();
|
||||||
let remote = repository.find_remote(name).unwrap();
|
let remote = repository.find_remote(name).unwrap();
|
||||||
let url = remote.url().unwrap();
|
let url = remote.url().unwrap();
|
||||||
|
|
||||||
(name.to_string(), url.to_string())
|
(name.to_string(), url.to_string())
|
||||||
}).collect();
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
let repo = Repo {
|
let repo = Repo { path, remotes };
|
||||||
path,
|
|
||||||
remotes,
|
|
||||||
};
|
|
||||||
repos.push(repo);
|
repos.push(repo);
|
||||||
} else if path.is_dir() && !hidden {
|
} 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)
|
Ok(repos)
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut repos = gather_repos(&start, 0)?;
|
let repos = {
|
||||||
repos.sort_unstable_by_key(|repo| repo.path.clone());
|
let mut r = gather_repos(&start)?;
|
||||||
for repo in &repos {
|
r.sort_unstable_by_key(|repo| repo.path.clone());
|
||||||
let path = repo.path.strip_prefix(&start).unwrap();
|
r
|
||||||
print!("Repository: {}", path.display().to_string().yellow());
|
};
|
||||||
|
|
||||||
print!(" ");
|
if flat {
|
||||||
for (name, url) in &repo.remotes {
|
print_flat(&repos, &start);
|
||||||
print!("[{name} {url}]");
|
} else {
|
||||||
}
|
print_indented(&repos, &start);
|
||||||
println!();
|
|
||||||
/*
|
|
||||||
let indent = recurse_level * INDENT_INCREMENT;
|
|
||||||
print!("{: <1$}", "", indent);
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
@@ -104,3 +104,66 @@ fn list_repos(directory: PathBuf) -> Result<(), std::io::Error> {
|
|||||||
|
|
||||||
Ok(())
|
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