aboutsummaryrefslogtreecommitdiff
path: root/src/repo/aggregate.rs
blob: cb4b00da07c168b4196d44695da7373c284fab2a (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
use git2::Repository;

use tracing::error;
use walkdir::WalkDir;

use crate::forge::Project;

use super::{Repo, Repos};

#[async_trait::async_trait]
pub trait Aggregator {
    async fn from_local(root: &str, scope: &str) -> Repos;
    async fn from_forge(root: &str, projects: Vec<Project>) -> Repos;
    async fn aggregate(mut local: Repos, mut remote: Repos) -> Repos;
}

#[async_trait::async_trait]
impl Aggregator for Repos {
    #[tracing::instrument(level = "trace")]
    async fn from_local(root: &str, scope: &str) -> Repos {
        let mut repos = Vec::new();

        let path: std::path::PathBuf = [root, scope].iter().collect();

        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() {
                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();

                    match Repository::open(entry.path()) {
                        Ok(repo) => repos.push(Repo {
                            name: entry
                                .path()
                                .strip_prefix(root)
                                .unwrap()
                                .to_str()
                                .unwrap()
                                .to_string(),
                            path: entry.path().to_path_buf(),
                            repo: Some(repo),
                            ..Repo::default()
                        }),
                        Err(err) => error!("could not open repository: {}", err),
                    }
                } else {
                    continue;
                }
            }
        }

        return repos;
    }

    #[tracing::instrument(level = "trace")]
    async 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
            })
            .collect()
    }

    #[tracing::instrument(level = "trace", skip(local, remote))]
    async fn aggregate(mut local: Repos, mut remote: Repos) -> Repos {
        local = local
            .into_iter()
            .map(|mut left| {
                if let Some(i) = remote.iter().position(|right| *right == left) {
                    let right = remote.remove(i);
                    left.forge = right.forge;
                }

                left
            })
            .collect();

        local.append(&mut remote);
        local.sort();

        return local;
    }
}