From b8c552fb6202315f6d3f1fbeb5edf2fd1fc7b79f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20K=C3=B6rber?= Date: Thu, 26 May 2022 16:30:32 +0200 Subject: [PATCH] Give repos a namespace to allow subdirectories --- src/config.rs | 17 ++++++++++++++--- src/grm/main.rs | 23 +++++++++++++++++------ src/lib.rs | 34 +++++++++++++++++++++++++--------- src/provider/github.rs | 12 ++++++------ src/provider/gitlab.rs | 12 ++++++------ src/provider/mod.rs | 15 ++++++++++----- src/repo.rs | 15 ++++++++++++++- 7 files changed, 92 insertions(+), 36 deletions(-) diff --git a/src/config.rs b/src/config.rs index 2a5648d..bbead78 100644 --- a/src/config.rs +++ b/src/config.rs @@ -5,7 +5,7 @@ use crate::output::*; use std::path::Path; -use crate::{get_token_from_command, Remote, Repo, Tree}; +use crate::{get_token_from_command, path_as_string, Remote, Repo, Tree}; use crate::provider; use crate::provider::Filter; @@ -104,8 +104,15 @@ impl RepoConfig { } pub fn into_repo(self) -> Repo { + let (namespace, name) = if let Some((namespace, name)) = self.name.rsplit_once('/') { + (Some(namespace.to_string()), name.to_string()) + } else { + (None, self.name) + }; + Repo { - name: self.name, + name, + namespace, worktree_setup: self.worktree_setup, remotes: self.remotes.map(|remotes| { remotes @@ -209,7 +216,11 @@ impl Config { .map(RepoConfig::from_repo) .collect(); let tree = ConfigTree { - root: crate::path_as_string(&Path::new(&config.root).join(namespace)), + root: if let Some(namespace) = namespace { + path_as_string(&Path::new(&config.root).join(namespace)) + } else { + path_as_string(Path::new(&config.root)) + }, repos: Some(repos), }; trees.push(tree); diff --git a/src/grm/main.rs b/src/grm/main.rs index 0f7c0c1..630ee67 100644 --- a/src/grm/main.rs +++ b/src/grm/main.rs @@ -82,10 +82,13 @@ fn main() { let mut trees: Vec = vec![]; for (namespace, repolist) in repos { - let tree = config::ConfigTree::from_repos( - Path::new(&args.root).join(namespace).display().to_string(), - repolist, - ); + let root = if let Some(namespace) = namespace { + path_as_string(&Path::new(&args.root).join(namespace)) + } else { + path_as_string(Path::new(&args.root)) + }; + + let tree = config::ConfigTree::from_repos(root, repolist); trees.push(tree); } @@ -310,7 +313,11 @@ fn main() { for (namespace, namespace_repos) in repos { let tree = config::ConfigTree { - root: path_as_string(&Path::new(&config.root).join(namespace)), + root: if let Some(namespace) = namespace { + path_as_string(&Path::new(&config.root).join(namespace)) + } else { + path_as_string(Path::new(&config.root)) + }, repos: Some( namespace_repos .into_iter() @@ -402,7 +409,11 @@ fn main() { for (namespace, repolist) in repos { let tree = config::ConfigTree { - root: Path::new(&args.root).join(namespace).display().to_string(), + root: if let Some(namespace) = namespace { + path_as_string(&Path::new(&args.root).join(namespace)) + } else { + path_as_string(Path::new(&args.root)) + }, repos: Some( repolist .into_iter() diff --git a/src/lib.rs b/src/lib.rs index 44957c2..c6322f8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -147,7 +147,7 @@ pub fn get_token_from_command(command: &str) -> Result { } fn sync_repo(root_path: &Path, repo: &Repo, init_worktree: bool) -> Result<(), String> { - let repo_path = root_path.join(&repo.name); + let repo_path = root_path.join(&repo.fullname()); let actual_git_directory = get_actual_git_directory(&repo_path, repo.worktree_setup); let mut newly_created = false; @@ -460,17 +460,33 @@ fn find_repos(root: &Path) -> Result, Vec, bool)>, Str } let remotes = results; - repos.push(Repo{ - name: match path == root { - true => match &root.parent() { + let (namespace, name) = if path == root { + ( + None, + match &root.parent() { Some(parent) => path_as_string(path.strip_prefix(parent).unwrap()), None => { warnings.push(String::from("Getting name of the search root failed. Do you have a git repository in \"/\"?")); - continue - }, - } - false => path_as_string(path.strip_prefix(&root).unwrap()), - }, + continue; + } + }, + ) + } else { + let name = path.strip_prefix(&root).unwrap(); + let namespace = name.parent().unwrap(); + ( + if namespace != Path::new("") { + Some(path_as_string(namespace).to_string()) + } else { + None + }, + path_as_string(name), + ) + }; + + repos.push(Repo { + name, + namespace, remotes: Some(remotes), worktree_setup: is_worktree, }); diff --git a/src/provider/github.rs b/src/provider/github.rs index 536f3c5..3a843c3 100644 --- a/src/provider/github.rs +++ b/src/provider/github.rs @@ -32,12 +32,12 @@ impl Project for GithubProject { self.name.clone() } - fn namespace(&self) -> String { - self.full_name - .rsplit_once('/') - .expect("Github project name did not include a namespace") - .0 - .to_string() + fn namespace(&self) -> Option { + if let Some((namespace, _name)) = self.full_name.rsplit_once('/') { + Some(namespace.to_string()) + } else { + None + } } fn ssh_url(&self) -> String { diff --git a/src/provider/gitlab.rs b/src/provider/gitlab.rs index 170141b..ccadd7f 100644 --- a/src/provider/gitlab.rs +++ b/src/provider/gitlab.rs @@ -39,12 +39,12 @@ impl Project for GitlabProject { self.name.clone() } - fn namespace(&self) -> String { - self.path_with_namespace - .rsplit_once('/') - .expect("Gitlab project name did not include a namespace") - .0 - .to_string() + fn namespace(&self) -> Option { + if let Some((namespace, _name)) = self.path_with_namespace.rsplit_once('/') { + Some(namespace.to_string()) + } else { + None + } } fn ssh_url(&self) -> String { diff --git a/src/provider/mod.rs b/src/provider/mod.rs index 04d8605..20253b6 100644 --- a/src/provider/mod.rs +++ b/src/provider/mod.rs @@ -35,6 +35,7 @@ pub trait Project { { Repo { name: self.name(), + namespace: self.namespace(), worktree_setup, remotes: Some(vec![Remote { name: String::from(provider_name), @@ -53,7 +54,7 @@ pub trait Project { } fn name(&self) -> String; - fn namespace(&self) -> String; + fn namespace(&self) -> Option; fn ssh_url(&self) -> String; fn http_url(&self) -> String; fn private(&self) -> bool; @@ -200,7 +201,7 @@ pub trait Provider { &self, worktree_setup: bool, force_ssh: bool, - ) -> Result>, String> { + ) -> Result, Vec>, String> { let mut repos = vec![]; if self.filter().owner { @@ -277,12 +278,16 @@ pub trait Provider { } } - let mut ret: HashMap> = HashMap::new(); + let mut ret: HashMap, Vec> = HashMap::new(); for repo in repos { - let namespace = repo.namespace().clone(); + let namespace = repo.namespace(); - let repo = repo.into_repo_config(&self.name(), worktree_setup, force_ssh); + let mut repo = repo.into_repo_config(&self.name(), worktree_setup, force_ssh); + + // Namespace is already part of the hashmap key. I'm not too happy + // about the data exchange format here. + repo.remove_namespace(); ret.entry(namespace).or_insert(vec![]).push(repo); } diff --git a/src/repo.rs b/src/repo.rs index 5ea257f..02d040f 100644 --- a/src/repo.rs +++ b/src/repo.rs @@ -114,11 +114,24 @@ pub struct Remote { #[derive(Debug)] pub struct Repo { pub name: String, - + pub namespace: Option, pub worktree_setup: bool, pub remotes: Option>, } +impl Repo { + pub fn fullname(&self) -> String { + match &self.namespace { + Some(namespace) => format!("{}/{}", namespace, self.name), + None => self.name.clone(), + } + } + + pub fn remove_namespace(&mut self) { + self.namespace = None + } +} + pub struct RepoChanges { pub files_new: usize, pub files_modified: usize,