feat: add repo tool
This commit is contained in:
commit
9b0f298bdc
5 changed files with 585 additions and 0 deletions
105
src/main.rs
Normal file
105
src/main.rs
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
use std::{
|
||||
env::{args, current_dir, home_dir, var},
|
||||
ffi::{OsStr, OsString},
|
||||
fs::create_dir_all,
|
||||
path::{Path, PathBuf},
|
||||
process::Command,
|
||||
};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use url::Url;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
const MAX_DEPTH: usize = 5;
|
||||
|
||||
struct SrcRoot<T: AsRef<Path> + AsRef<OsStr>> {
|
||||
path: T,
|
||||
}
|
||||
|
||||
impl<T: AsRef<Path> + AsRef<OsStr>> SrcRoot<T> {
|
||||
fn clone_repo(&self, url: &Url) -> Result<()> {
|
||||
let url = OsString::from(url.as_ref());
|
||||
Command::new("git")
|
||||
.args([
|
||||
OsString::from("clone").as_os_str(),
|
||||
url.as_os_str(),
|
||||
self.path.as_ref(),
|
||||
])
|
||||
.status()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn as_repo_path(&self, url: &Url) -> PathBuf {
|
||||
let mut dir = PathBuf::new();
|
||||
dir.push::<&Path>(self.path.as_ref());
|
||||
dir.push(url.host_str().unwrap_or("misc"));
|
||||
url.path_segments()
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.filter(|s| !s.is_empty())
|
||||
.for_each(|s| dir.push(s.trim_end_matches(".git")));
|
||||
dir
|
||||
}
|
||||
|
||||
fn ensure_repo_checkout(&self, url: &Url) -> Result<PathBuf> {
|
||||
let repo_path = self.as_repo_path(url);
|
||||
if !repo_path.is_dir() || repo_path.read_dir()?.count() == 0 {
|
||||
create_dir_all(&repo_path)?;
|
||||
self.clone_repo(url)?;
|
||||
}
|
||||
Ok(repo_path)
|
||||
}
|
||||
|
||||
fn search_repo<S: AsRef<OsStr>>(&self, slug: S) -> Result<Option<PathBuf>> {
|
||||
let walker = WalkDir::new(&self.path)
|
||||
.follow_links(false)
|
||||
.max_depth(MAX_DEPTH)
|
||||
.follow_root_links(false)
|
||||
.max_open(1)
|
||||
.sort_by_file_name()
|
||||
.contents_first(false)
|
||||
.same_file_system(true);
|
||||
for path in walker {
|
||||
let p = path?;
|
||||
let git = {
|
||||
let mut g = p.clone().into_path();
|
||||
g.push(".git");
|
||||
g
|
||||
};
|
||||
if let Some(file_name) = p.path().file_name() {
|
||||
if file_name == slug.as_ref() && git.is_dir() {
|
||||
return Ok(Some(p.into_path()));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn new(path: T) -> Self {
|
||||
Self { path }
|
||||
}
|
||||
}
|
||||
|
||||
fn src_root() -> Result<PathBuf> {
|
||||
if let Ok(src_root) = var("REPO_SRC_ROOT").map(PathBuf::from) {
|
||||
Ok(src_root)
|
||||
} else {
|
||||
let mut src_root = home_dir().unwrap_or(current_dir()?);
|
||||
src_root.push("git");
|
||||
Ok(src_root)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let arg = args().next_back().context("Missing URL / name")?;
|
||||
let src_root = src_root()?;
|
||||
let src_root = SrcRoot::new(src_root);
|
||||
let path = if let Ok(url) = Url::parse(&arg) {
|
||||
src_root.ensure_repo_checkout(&url)
|
||||
} else {
|
||||
let slug: OsString = arg.into();
|
||||
src_root.search_repo(slug)?.context("Repo not found")
|
||||
}?;
|
||||
println!("{}", path.display());
|
||||
Ok(())
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue