1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
use std::{collections::HashMap, os::unix::ffi::OsStrExt, sync::RwLock};
use gix::bstr::ByteSlice;
use tracing::{debug, error};
use walkdir::WalkDir;
use crate::forge::Project;
use super::{Repo, Repos};
#[async_trait::async_trait]
pub trait Aggregator {
fn from_local(root: &str, scope: &str) -> Repos;
fn from_forge(root: &str, projects: Vec<Project>) -> Repos;
fn aggregate(local: Repos, remote: Repos) -> Repos;
}
#[async_trait::async_trait]
impl Aggregator for Repos {
#[tracing::instrument(level = "trace")]
fn from_local(root: &str, scope: &str) -> Repos {
let mut repos = HashMap::new();
let path: std::path::PathBuf = root.into();
if !path.exists() {
return repos;
}
let mut walker = WalkDir::new(path).into_iter();
loop {
let entry = match walker.next() {
None => break,
Some(Err(err)) => panic!("ERROR: {}", err),
Some(Ok(entry)) => entry,
};
if entry.file_type().is_dir() && entry.path().as_os_str().as_bytes().contains_str(scope)
{
let mut dir = std::fs::read_dir(entry.path()).unwrap();
if dir.any(|dir| {
if let Ok(dir) = dir {
dir.file_name() == ".git"
} else {
false
}
}) {
walker.skip_current_dir();
debug!("found git repo {:?} trying to open...", entry.path());
match gix::open(entry.path()) {
Ok(repo) => {
let name = entry
.path()
.strip_prefix(root)
.unwrap()
.to_str()
.unwrap()
.to_string();
repos.insert(
name.clone(),
RwLock::new(Repo {
name,
path: entry.path().to_path_buf(),
repo: Some(repo),
..Repo::default()
}),
);
}
Err(err) => error!("could not open repository: {}", err),
}
} else {
continue;
}
}
}
repos
}
#[tracing::instrument(level = "trace")]
fn from_forge(root: &str, projects: Vec<Project>) -> Repos {
projects
.iter()
.map(|project| {
let mut repo: Repo = project.into();
repo.path = [root, &repo.name].iter().collect();
(repo.name.clone(), RwLock::new(repo))
})
.collect()
}
// TODO optimise this func
//
// the iteration is currently quite inefficient as
// it's constantly removing stuff from `remote`
#[tracing::instrument(level = "trace", skip(local, remote))]
fn aggregate(mut local: Repos, mut remote: Repos) -> Repos {
local = local
.into_iter()
.map(|(left_name, left)| {
if let Some(right) = remote.remove(&left_name) {
left.write().unwrap().forge = right.into_inner().unwrap().forge;
}
(left_name, left)
})
.collect();
local.extend(remote.into_iter());
// local.sort();
local
}
}
|