1use crate::error::ExecutionError;
2use std::path::Path;
3use std::process::{Command, Output, Stdio};
4
5pub struct GitCommandExecuter<'a> {
6 git_storage_folder_path: &'a Path,
7 commit_message: String,
8 remote_link: String,
9 run_init: bool,
10 run_remote_add: bool,
11 run_remote_remove: bool,
12 run_pull: bool,
13 run_commit: bool,
14}
15
16pub struct GitCommandExecuterBuilder<'a> {
17 git_storage_folder_path: &'a Path,
18 commit_message: String,
19 remote_link: String,
20 run_init: bool,
21 run_remote_add: bool,
22 run_remote_remove: bool,
23 run_pull: bool,
24 run_commit: bool,
25}
26
27struct GitError {
28 command: &'static str,
29 err: String,
30}
31
32impl From<GitError> for ExecutionError {
33 fn from(err: GitError) -> Self {
34 ExecutionError::GitCommand {
35 command: err.command,
36 err: err.err,
37 }
38 }
39}
40
41impl GitCommandExecuter<'_> {
42 pub fn run(self) -> Result<(), ExecutionError> {
43 if self.run_init && !init(self.git_storage_folder_path)?.status.success() {
44 return Err(ExecutionError::NoSuccess("git init"));
45 }
46
47 if self.run_remote_remove {
48 if !remote_remove(self.git_storage_folder_path)?
49 .status
50 .success()
51 {
52 return Err(ExecutionError::NoSuccess("git remote remove"));
53 }
54
55 return Ok(());
56 }
57
58 if self.run_remote_add
59 && !remote_add(self.git_storage_folder_path, &self.remote_link)?
60 .status
61 .success()
62 {
63 return Err(ExecutionError::NoSuccess("git remote add"));
64 }
65
66 if self.run_pull && self.run_remote_add {
67 let output = pull(self.git_storage_folder_path)?;
68
69 if output.status.success() {
70 return Ok(());
71 }
72
73 let output = pull(self.git_storage_folder_path)?.stderr;
74
75 let Ok(output) = String::from_utf8(output) else {
76 return Err(ExecutionError::InvalidUTF8("Convert Vec<u8> to String"));
77 };
78
79 if output.contains("Repository not found") {
80 return Err(ExecutionError::RepositoryNotFound);
81 }
82 } else if self.run_pull || self.run_commit {
83 let output = pull(self.git_storage_folder_path)?;
84
85 if !output.status.success() {
86 let output = pull(self.git_storage_folder_path)?.stderr;
87
88 let Ok(output) = String::from_utf8(output) else {
89 return Err(ExecutionError::InvalidUTF8("Convert Vec<u8> to String"));
90 };
91
92 if !output.contains("couldn't find remote ref main") {
93 return Err(ExecutionError::NoSuccess("git pull"));
94 }
95 }
96 }
97
98 if !self.run_commit {
99 return Ok(());
100 }
101
102 if !add_all(self.git_storage_folder_path)?.status.success() {
103 return Err(ExecutionError::NoSuccess("git add"));
104 }
105
106 if !commit(self.git_storage_folder_path, &self.commit_message)?
107 .status
108 .success()
109 {
110 return Err(ExecutionError::NoSuccess("git commit"));
111 }
112
113 if !push(self.git_storage_folder_path)?.status.success() {
114 return Err(ExecutionError::NoSuccess("git push"));
115 }
116
117 Ok(())
118 }
119}
120
121impl<'a> GitCommandExecuterBuilder<'a> {
122 pub fn new(git_storage_folder_path: &'a Path) -> Self {
123 GitCommandExecuterBuilder {
124 git_storage_folder_path,
125 run_init: false,
126 commit_message: String::new(),
127 remote_link: String::new(),
128 run_commit: false,
129 run_remote_add: false,
130 run_pull: false,
131 run_remote_remove: false,
132 }
133 }
134
135 pub fn run_init(mut self) -> Self {
136 self.run_init = true;
137
138 self
139 }
140
141 pub fn run_commit(mut self, commit_message: impl Into<String>) -> Self {
142 self.run_commit = true;
143 self.commit_message = commit_message.into();
144
145 self
146 }
147
148 pub fn run_pull(mut self) -> Self {
149 self.run_pull = true;
150
151 self
152 }
153
154 pub fn run_remote_remove(mut self) -> Self {
155 self.run_remote_remove = true;
156
157 self
158 }
159
160 pub fn run_remote_add(mut self, link: impl Into<String>) -> Self {
161 self.run_remote_add = true;
162 self.remote_link = link.into();
163
164 self
165 }
166
167 pub fn build(self) -> GitCommandExecuter<'a> {
168 GitCommandExecuter {
169 git_storage_folder_path: self.git_storage_folder_path,
170 run_init: self.run_init,
171 commit_message: self.commit_message,
172 remote_link: self.remote_link,
173 run_commit: self.run_commit,
174 run_remote_add: self.run_remote_add,
175 run_pull: self.run_pull,
176 run_remote_remove: self.run_remote_remove,
177 }
178 }
179}
180
181fn init(git_storage_folder_path: &Path) -> Result<Output, GitError> {
182 let output = match Command::new("git")
183 .arg("init")
184 .current_dir(git_storage_folder_path)
185 .stdout(Stdio::null())
186 .stderr(Stdio::null())
187 .output()
188 {
189 Ok(output) => output,
190 Err(err) => {
191 return Err(GitError {
192 command: "init",
193 err: err.to_string(),
194 });
195 }
196 };
197
198 Ok(output)
199}
200
201fn add_all(git_storage_folder_path: &Path) -> Result<Output, GitError> {
202 let output = match Command::new("git")
203 .args(["add", "."])
204 .current_dir(git_storage_folder_path)
205 .stdout(Stdio::null())
206 .stderr(Stdio::null())
207 .output()
208 {
209 Ok(output) => output,
210 Err(err) => {
211 return Err(GitError {
212 command: "add",
213 err: err.to_string(),
214 });
215 }
216 };
217
218 Ok(output)
219}
220
221fn commit(git_storage_folder_path: &Path, commit_name: &str) -> Result<Output, GitError> {
222 let output = match Command::new("git")
223 .args(["commit", "-m", commit_name])
224 .current_dir(git_storage_folder_path)
225 .stdout(Stdio::null())
226 .stderr(Stdio::null())
227 .output()
228 {
229 Ok(output) => output,
230 Err(err) => {
231 return Err(GitError {
232 command: "commit",
233 err: err.to_string(),
234 });
235 }
236 };
237
238 Ok(output)
239}
240
241fn push(git_storage_folder_path: &Path) -> Result<Output, GitError> {
242 let output = match Command::new("git")
243 .args(["push", "origin", "main"])
244 .current_dir(git_storage_folder_path)
245 .stdout(Stdio::null())
246 .stderr(Stdio::null())
247 .output()
248 {
249 Ok(output) => output,
250 Err(err) => {
251 return Err(GitError {
252 command: "push",
253 err: err.to_string(),
254 });
255 }
256 };
257
258 Ok(output)
259}
260
261fn remote_remove(git_storage_folder_path: &Path) -> Result<Output, GitError> {
262 let output = match Command::new("git")
263 .args(["remote", "remove", "origin"])
264 .current_dir(git_storage_folder_path)
265 .stdout(Stdio::null())
266 .stderr(Stdio::null())
267 .output()
268 {
269 Ok(output) => output,
270 Err(err) => {
271 return Err(GitError {
272 command: "remote remove",
273 err: err.to_string(),
274 });
275 }
276 };
277
278 Ok(output)
279}
280
281fn remote_add(git_storage_folder_path: &Path, link: &str) -> Result<Output, GitError> {
282 let output = match Command::new("git")
283 .args(["remote", "add", "origin", link])
284 .current_dir(git_storage_folder_path)
285 .stdout(Stdio::null())
286 .stderr(Stdio::null())
287 .output()
288 {
289 Ok(output) => output,
290 Err(err) => {
291 return Err(GitError {
292 command: "remote add",
293 err: err.to_string(),
294 });
295 }
296 };
297
298 Ok(output)
299}
300
301fn pull(git_storage_folder_path: &Path) -> Result<Output, GitError> {
302 let output = match Command::new("git")
303 .args(["pull", "origin", "main"])
304 .current_dir(git_storage_folder_path)
305 .stdout(Stdio::piped())
306 .stderr(Stdio::piped())
307 .output()
308 {
309 Ok(output) => output,
310 Err(err) => {
311 return Err(GitError {
312 command: "git pull",
313 err: err.to_string(),
314 });
315 }
316 };
317
318 Ok(output)
319}