From 9628bf9b71b090bb30b7e00e87d63ce90597df58 Mon Sep 17 00:00:00 2001 From: Tim Schubert Date: Sat, 9 Aug 2025 23:58:19 +0200 Subject: [PATCH] chore: refactor into lib --- src/lib.rs | 5 ++ src/main.rs | 163 ++++++++++++--------------------------------------- src/root.rs | 91 ++++++++++++++++++++++++++++ src/shell.rs | 15 +++++ 4 files changed, 148 insertions(+), 126 deletions(-) create mode 100644 src/lib.rs create mode 100644 src/root.rs create mode 100644 src/shell.rs diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..64a7fe9 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,5 @@ +mod root; +mod shell; + +pub use root::SrcRoot; +pub use shell::setup_shell; diff --git a/src/main.rs b/src/main.rs index 06a8df4..07be249 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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 + AsRef> { - root_path: T, -} - -impl + AsRef> 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(()) + #[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 { - 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>(&self, slug: S) -> Result> { - 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, + repo: String, + }, + Setup { + #[arg(long)] + root: Option, + }, } } -fn src_root() -> Result { - if let Ok(src_root) = var("REPO_CODE_ROOT").map(PathBuf::from) { - Ok(src_root) +fn resolve_root(root: Option) -> Result { + 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, - repo: String, - }, - Setup { - #[arg(long)] - root: Option, - }, + 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); } } diff --git a/src/root.rs b/src/root.rs new file mode 100644 index 0000000..259f692 --- /dev/null +++ b/src/root.rs @@ -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 { + 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>(&self, slug: S) -> Result> { + 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> { + 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 } + } +} diff --git a/src/shell.rs b/src/shell.rs new file mode 100644 index 0000000..7c8684d --- /dev/null +++ b/src/shell.rs @@ -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() + ); +}