chore: refactor into lib

This commit is contained in:
Tim Schubert 2025-08-09 23:58:19 +02:00
parent 430d988f31
commit 9628bf9b71
No known key found for this signature in database
4 changed files with 148 additions and 126 deletions

5
src/lib.rs Normal file
View file

@ -0,0 +1,5 @@
mod root;
mod shell;
pub use root::SrcRoot;
pub use shell::setup_shell;

View file

@ -1,153 +1,64 @@
use std::{
env::{current_dir, home_dir, var},
ffi::{OsStr, OsString},
fs::create_dir_all,
path::{Path, PathBuf},
process::Command,
path::PathBuf,
};
use anyhow::{Context, Result};
use clap::{Parser, Subcommand};
use url::Url;
use walkdir::WalkDir;
use anyhow::Result;
use clap::Parser;
use repo_rs::{SrcRoot, setup_shell};
const MAX_DEPTH: usize = 5;
mod cli {
use std::path::PathBuf;
fn setup_shell(code_root: &Path) {
println!(
r#"
h() {{
_h_dir=$(command repo resolve --root "{}" "$@")
_h_ret=$?
[ "$_h_dir" != "$PWD" ] && pushd "$_h_dir"
return $_h_ret
}}
"#,
code_root.display()
);
}
use clap::{Parser, Subcommand};
struct SrcRoot<T: AsRef<Path> + AsRef<OsStr>> {
root_path: T,
}
impl<T: AsRef<Path> + AsRef<OsStr>> SrcRoot<T> {
fn clone_repo(url: &Url, destination: &Path) -> Result<()> {
let url = OsString::from(url.as_ref());
Command::new("git")
.args([
OsString::from("clone").as_os_str(),
url.as_os_str(),
destination.as_os_str(),
])
.status()?;
Ok(())
#[derive(Parser)]
#[command(version, about, long_about = None)]
#[command(propagate_version = true)]
pub(crate) struct Cli {
#[command(subcommand)]
pub(crate) command: Commands,
}
fn as_repo_path(&self, url: &Url) -> PathBuf {
let mut dir = PathBuf::new();
dir.push::<&Path>(self.root_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() {
create_dir_all(&repo_path)?;
}
Self::clone_repo(url, &repo_path)?;
Ok(repo_path)
}
fn search_repo<S: AsRef<OsStr>>(&self, slug: S) -> Result<Option<PathBuf>> {
let walker = WalkDir::new(&self.root_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 { root_path: path }
#[derive(Subcommand)]
pub(crate) enum Commands {
Resolve {
#[arg(long)]
root: Option<PathBuf>,
repo: String,
},
Setup {
#[arg(long)]
root: Option<PathBuf>,
},
}
}
fn src_root() -> Result<PathBuf> {
if let Ok(src_root) = var("REPO_CODE_ROOT").map(PathBuf::from) {
Ok(src_root)
fn resolve_root(root: Option<PathBuf>) -> Result<PathBuf> {
let path_buf = if let Some(root) = root {
root
} else if let Ok(src_root) = var("REPO_CODE_ROOT").map(PathBuf::from) {
src_root
} else {
let mut src_root = home_dir().unwrap_or(current_dir()?);
src_root.push("code");
Ok(src_root)
}
}
fn resolve_repo(arg: String, src_root: PathBuf) -> Result<(), anyhow::Error> {
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(())
}
#[derive(Parser)]
#[command(version, about, long_about = None)]
#[command(propagate_version = true)]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
Resolve {
#[arg(long)]
root: Option<PathBuf>,
repo: String,
},
Setup {
#[arg(long)]
root: Option<PathBuf>,
},
src_root
};
Ok(path_buf)
}
fn main() -> Result<()> {
use crate::cli::{Cli, Commands};
let cli = Cli::parse();
match cli.command {
Commands::Resolve { root, repo } => {
let root = root.context("").or_else(|_e| src_root())?;
resolve_repo(repo, root)?;
let root = SrcRoot::new(resolve_root(root)?);
println!("{}", root.resolve_repo(repo)?.as_ref().display());
}
Commands::Setup { root } => {
let root = root.context("").or_else(|_e| src_root())?;
let root = resolve_root(root)?;
setup_shell(&root);
}
}

91
src/root.rs Normal file
View file

@ -0,0 +1,91 @@
use anyhow::{Context, Result};
use std::{
ffi::{OsStr, OsString},
fs::create_dir_all,
path::{Path, PathBuf},
process::Command,
};
use url::Url;
use walkdir::WalkDir;
const MAX_DEPTH: usize = 5;
pub struct SrcRoot {
root_path: PathBuf,
}
impl SrcRoot {
fn clone_repo(url: &Url, destination: &Path) -> Result<()> {
let url = OsString::from(url.as_ref());
Command::new("git")
.args([
OsString::from("clone").as_os_str(),
url.as_os_str(),
destination.as_os_str(),
])
.status()?;
Ok(())
}
fn as_repo_path(&self, url: &Url) -> PathBuf {
let mut dir = PathBuf::new();
dir.push::<&Path>(self.root_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() {
create_dir_all(&repo_path)?;
}
Self::clone_repo(url, &repo_path)?;
Ok(repo_path)
}
fn search_repo<S: AsRef<OsStr>>(&self, slug: S) -> Result<Option<PathBuf>> {
let walker = WalkDir::new(&self.root_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)
}
/// # Errors
/// File system errors
pub fn resolve_repo(&self, arg: String) -> Result<impl AsRef<Path>> {
if let Ok(url) = Url::parse(&arg) {
self.ensure_repo_checkout(&url)
} else {
let slug: OsString = arg.into();
self.search_repo(slug)?.context("Repo not found")
}
}
#[must_use]
pub fn new(path: PathBuf) -> Self {
Self { root_path: path }
}
}

15
src/shell.rs Normal file
View file

@ -0,0 +1,15 @@
use std::path::Path;
pub fn setup_shell(code_root: &Path) {
println!(
r#"
h() {{
_h_dir=$(command repo resolve --root "{}" "$@")
_h_ret=$?
[ "$_h_dir" != "$PWD" ] && pushd "$_h_dir"
return $_h_ret
}}
"#,
code_root.display()
);
}