use std::fs;
use std::path::Path;
use crate::config::{self, Config};
use crate::repo::{self, Repo};
pub struct SearchOptions {
pub search_folders: Vec<String>,
pub query: String,
}
pub fn for_repo(config: &Config, query: &str) -> SearchOptions {
SearchOptions {
search_folders: config::get_search_folders(config),
query: query.to_string(),
}
}
pub fn in_folders(folders: Vec<String>, query: &str) -> SearchOptions {
SearchOptions {
search_folders: folders,
query: query.to_string(),
}
}
pub fn default_options(config: &Config) -> SearchOptions {
SearchOptions {
search_folders: config::get_search_folders(config),
query: String::new(),
}
}
pub fn repositories(opts: &SearchOptions, config: &Config) -> Vec<Repo> {
let pins = config::get_pinned_repos(config);
let mut found: Vec<Repo> = opts
.search_folders
.iter()
.flat_map(|folder| find_repos_recursively(folder, &opts.query, &pins))
.collect();
found.sort_by(|a, b| a.name.cmp(&b.name));
found
}
pub fn sort_by_pins(repos: &mut [Repo]) {
repos.sort_by(|a, b| match (a.pinned, b.pinned) {
(true, false) => std::cmp::Ordering::Less,
(false, true) => std::cmp::Ordering::Greater,
(true, true) => a.pin_index.cmp(&b.pin_index),
(false, false) => a.name.cmp(&b.name),
});
}
pub fn find_repo_for_path(path: &str, config: &Config) -> Option<Repo> {
let opts = default_options(config);
let repos = repositories(&opts, config);
repos.into_iter().find(|r| path.starts_with(&r.path))
}
fn find_repos_recursively(folder: &str, query: &str, pins: &[String]) -> Vec<Repo> {
let entries = match fs::read_dir(folder) {
Ok(e) => e,
Err(e) => {
eprintln!("ERROR Error reading directory \"{folder}\": {e}");
return Vec::new();
}
};
let mut results = Vec::new();
for entry in entries.flatten() {
let path = entry.path();
if !path.is_dir() {
continue;
}
let name = entry.file_name().to_string_lossy().to_string();
let path_str = path.to_string_lossy().to_string();
if is_git_repo(&path) {
if name.contains(query) {
let pin_index = pins.iter().position(|p| p == &path_str);
let r = match pin_index {
Some(idx) => repo::new_pinned(&path_str, &name, idx as i32),
None => repo::new(&path_str, &name),
};
results.push(r);
}
} else {
results.extend(find_repos_recursively(&path_str, query, pins));
}
}
results
}
fn is_git_repo(path: &Path) -> bool {
path.join(".git").exists()
}