summaryrefslogtreecommitdiff
path: root/lib/libgfold/src/collector.rs
blob: ca8068229ace86dda0e5ab5f475873a4e74cf79d (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
//! This module contains the functionality for generating reports.

use rayon::prelude::*;
use std::collections::BTreeMap;
use std::path::Path;
use target::TargetCollector;
use thiserror::Error;

use crate::repository_view::{RepositoryView, RepositoryViewError, RepositoryViewResult};

mod target;

#[allow(missing_docs)]
#[derive(Error, Debug)]
pub enum CollectorError {
    #[error(transparent)]
    FromRepositoryView(#[from] RepositoryViewError),
    #[error(transparent)]
    FromStdIo(#[from] std::io::Error),
}

/// The result type used when multiple kinds of errors can be encountered during collection.
pub type CollectorResult<T> = Result<T, CollectorError>;

/// This type represents a [`BTreeMap`] using an optional [`String`] for keys, which represents the
/// parent directory for a group of reports ([`Vec<RepositoryView>`]). The values corresponding to those keys
/// are the actual groups of reports.
///
/// We use a [`BTreeMap`] instead of a [`HashMap`](std::collections::HashMap) in order to have
/// sorted keys.
pub type RepositoryCollection = BTreeMap<Option<String>, Vec<RepositoryView>>;

type UnprocessedRepositoryView = RepositoryViewResult<RepositoryView>;

/// A unit struct that provides [`Self::run()`], which is used to generated [`RepositoryCollection`].
#[derive(Debug)]
pub struct RepositoryCollector;

impl RepositoryCollector {
    /// Generate [`RepositoryCollection`] for a given path and its children.
    pub fn run(
        path: &Path,
        include_email: bool,
        include_submodules: bool,
    ) -> CollectorResult<RepositoryCollection> {
        let unprocessed = TargetCollector::run(path.to_path_buf())?
            .par_iter()
            .map(|path| RepositoryView::new(path, include_email, include_submodules))
            .collect::<Vec<UnprocessedRepositoryView>>();

        let mut processed = RepositoryCollection::new();
        for maybe_view in unprocessed {
            match maybe_view {
                Ok(view) => {
                    if let Some(mut views) =
                        processed.insert(view.parent.clone(), vec![view.clone()])
                    {
                        views.push(view.clone());
                        processed.insert(view.parent, views);
                    }
                }
                Err(e) => return Err(e.into()),
            }
        }
        Ok(processed)
    }
}