dfmn/
git.rs

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}