aboutsummaryrefslogtreecommitdiff
path: root/src/repo/aggregate.rs
blob: f8e44a5f96ef226fbe6b27a7c3115e3bce20e240 (plain)
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
    }
}