gitai/remote/common/
sequence.rs1use 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}