gitai/remote/common/
sequence.rs

1use std::sync::Arc;
2
3use cause::Cause;
4use cause::cause;
5use temp_dir::TempDir;
6
7use super::ErrorType;
8use super::ErrorType::NoItemToOperate;
9use super::Parsed;
10use super::Target;
11
12pub enum Mode {
13    Single,
14    Parallel,
15}
16
17pub trait Operation {
18    fn operate(
19        &self,
20        prefix: &str,
21        parsed: &Parsed,
22        rootdir: &str,
23        tempdir: &TempDir,
24    ) -> Result<bool, Cause<ErrorType>>;
25}
26
27pub fn sequence(
28    target: Target,
29    operation: &Arc<dyn Operation + Send + Sync>,
30    mode: &Mode,
31) -> Result<bool, Cause<ErrorType>> {
32    let (rootdir, parsed): (String, Vec<_>) = match target {
33        Target::Declared(Some(ref name)) => {
34            let (rootdir, parsed) = super::parse::parse_gitwire()?;
35            let parsed = parsed
36                .into_iter()
37                .filter(|p| match p.name {
38                    Some(ref n) => n == name,
39                    None => false,
40                })
41                .collect();
42            (rootdir, parsed)
43        }
44        Target::Declared(None) => super::parse::parse_gitwire()?,
45        Target::Direct(parsed) => (
46            std::env::current_dir()
47                .or(Err(cause!(ErrorType::CurrentDirRetrieve)))?
48                .into_os_string()
49                .into_string()
50                .or(Err(cause!(ErrorType::CurrentDirConvert)))?,
51            vec![parsed],
52        ),
53    };
54
55    let len = parsed.len();
56    if len == 0 {
57        Err(cause!(NoItemToOperate, "There are no items to operate."))?;
58    }
59
60    match mode {
61        Mode::Single => single(parsed.as_slice(), rootdir.as_str(), operation.as_ref()),
62        Mode::Parallel => parallel(parsed, rootdir.as_str(), operation),
63    }
64}
65
66fn single(
67    parsed: &[Parsed],
68    rootdir: &str,
69    operation: &dyn Operation,
70) -> Result<bool, Cause<ErrorType>> {
71    let len = parsed.len();
72
73    let mut result = true;
74    for (i, parsed) in parsed.iter().enumerate() {
75        println!(">> {}/{} started{}", i + 1, len, additional_message(parsed));
76        let tempdir = super::fetch::fetch_target_to_tempdir("", parsed)?;
77        let success = operation.operate("", parsed, rootdir, &tempdir)?;
78        if !success {
79            result = false;
80        }
81    }
82    println!(">> All check tasks have done!\n");
83    Ok(result)
84}
85
86fn parallel(
87    parsed: Vec<Parsed>,
88    rootdir: &str,
89    operation: &Arc<dyn Operation + Send + Sync>,
90) -> Result<bool, Cause<ErrorType>> {
91    use colored::Colorize;
92
93    let len = parsed.len();
94    let operation = operation.clone();
95
96    let results: Vec<_> = std::thread::scope(|s| {
97        let results: Vec<_> = parsed
98            .into_iter()
99            .enumerate()
100            .map(|(i, parsed)| {
101                s.spawn({
102                    let operation = operation.clone();
103                    move || -> Result<bool, Cause<ErrorType>> {
104                        let prefix = format!("No.{i} ");
105                        println!(
106                            "{}",
107                            format!(
108                                ">> {prefix}({}/{len}) started{}",
109                                i + 1,
110                                additional_message(&parsed)
111                            )
112                            .blue()
113                        );
114                        let tempdir = super::fetch::fetch_target_to_tempdir(&prefix, &parsed)?;
115                        let success = operation.operate(&prefix, &parsed, rootdir, &tempdir)?;
116                        if success {
117                            println!(
118                                "{}",
119                                format!(
120                                    ">> {prefix}({}/{len}) succeeded{}",
121                                    i + 1,
122                                    additional_message(&parsed)
123                                )
124                                .blue()
125                            );
126                            Ok(true)
127                        } else {
128                            println!(
129                                "{}",
130                                format!(
131                                    ">> {prefix}({}/{len}) failed{}",
132                                    i + 1,
133                                    additional_message(&parsed)
134                                )
135                                .magenta()
136                            );
137                            Ok(false)
138                        }
139                    }
140                })
141            })
142            .collect();
143        results
144            .into_iter()
145            .map(|h| h.join().expect("A thread panicked during execution"))
146            .collect()
147    });
148    println!("{}", ">> All check tasks have done!\n".to_string().blue());
149
150    let result = if results.iter().any(|r| matches!(r, Ok(false))) {
151        Ok(false)
152    } else {
153        Ok(true)
154    };
155    if let Some(err) = results.into_iter().find(|r| matches!(r, Err(..))) {
156        return err;
157    }
158    result
159}
160
161fn additional_message(parsed: &Parsed) -> String {
162    match (&parsed.name, &parsed.dsc) {
163        (Some(name), Some(dsc)) => format!(" ({name}: {dsc})"),
164        (Some(name), None) => format!(" ({name})"),
165        (None, Some(dsc)) => format!(" ({dsc})"),
166        (None, None) => String::new(),
167    }
168}