1#[cfg(test)]
2use mockall::automock;
3
4use crate::console_output::ConsoleOutput;
5use crate::default_error::DefaultError;
6use crate::note::Note;
7use crate::shell::Shell;
8
9#[cfg_attr(test, automock)]
10pub trait Git {
11 fn init(&self) -> Result<ConsoleOutput, DefaultError>;
12 fn commit(&self, note: &Note, message: &str) -> Result<ConsoleOutput, DefaultError>;
13 fn has_changed(&self, note: &Note) -> bool;
14 fn push(&self) -> Result<ConsoleOutput, DefaultError>;
15 fn pull(&self) -> Result<ConsoleOutput, DefaultError>;
16}
17
18pub struct GitImpl<'a> {
19 shell: &'a dyn Shell,
20}
21
22impl<'a> GitImpl<'a> {
23 pub fn new(shell: &'a dyn Shell) -> GitImpl<'a> {
24 GitImpl { shell }
25 }
26}
27
28impl<'a> Git for GitImpl<'a> {
29 fn init(&self) -> Result<ConsoleOutput, DefaultError> {
30 match self.shell.execute_in_repo("git init") {
31 Ok(o) => Ok(o.into()),
32 Err(e) => Err(e),
33 }
34 }
35
36 fn commit(&self, note: &Note, message: &str) -> Result<ConsoleOutput, DefaultError> {
37 let path = ¬e.path.to_str().unwrap();
38 let mut out = ConsoleOutput::empty();
39 out.append_command_output(self.shell.execute_in_repo(format!("git add '{}'", path).as_str())?);
40 out.append_command_output(self.shell.execute_in_repo(format!("git commit -m '{}' '{}'", message, path).as_str())?);
41 Ok(out)
42 }
43
44 fn has_changed(&self, note: &Note) -> bool {
45 let path = note.path.to_str().unwrap();
46 self.shell
47 .execute_in_repo(format!("git add '{p}' && git diff --exit-code HEAD '{p}' > /dev/null", p = path).as_str())
48 .is_err()
49 }
50
51 fn push(&self) -> Result<ConsoleOutput, DefaultError> {
52 match self.shell.execute_interactive_in_repo("git push") {
53 Ok(o) => Ok(o.into()),
54 Err(e) => Err(e),
55 }
56 }
57
58 fn pull(&self) -> Result<ConsoleOutput, DefaultError> {
59 match self.shell.execute_interactive_in_repo("git pull") {
60 Ok(o) => Ok(o.into()),
61 Err(e) => Err(e),
62 }
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69 use crate::shell::{CommandOutput, MockShell};
70 use std::path::PathBuf;
71
72 fn test_note() -> Note {
73 Note::from(0, PathBuf::from("/repository/test.md"), "# Title\nContent\n".to_string()).unwrap()
74 }
75
76 #[test]
77 fn init() {
78 let mut shell_mock = MockShell::new();
79 let exp_command = "git init";
80 shell_mock
81 .expect_execute_in_repo()
82 .times(1)
83 .withf(move |c| c == exp_command)
84 .returning(|_| Ok(CommandOutput::default()));
85
86 let git = GitImpl::new(&shell_mock);
87 git.init().unwrap();
88 }
89
90 #[test]
91 fn commit() {
92 let note = test_note();
93
94 let mut shell_mock = MockShell::new();
95 let exp_command = format!("git add '{}'", note.path.to_str().unwrap());
96 shell_mock
97 .expect_execute_in_repo()
98 .times(1)
99 .withf(move |c| c == exp_command)
100 .returning(|_| Ok(CommandOutput::default()));
101
102 let exp_command = format!("git commit -m 'message' '{}'", note.path.to_str().unwrap());
103 shell_mock
104 .expect_execute_in_repo()
105 .times(1)
106 .withf(move |c| c == exp_command)
107 .returning(|_| Ok(CommandOutput::default()));
108
109 let git = GitImpl::new(&shell_mock);
110 git.commit(¬e, "message").unwrap();
111 }
112
113 #[test]
114 fn has_changed() {
115 let note = test_note();
116
117 let mut shell_mock = MockShell::new();
118 let exp_command = format!("git add '{p}' && git diff --exit-code HEAD '{p}' > /dev/null", p = note.path.to_str().unwrap());
119 shell_mock
120 .expect_execute_in_repo()
121 .times(1)
122 .withf(move |c| c == exp_command)
123 .returning(|_| Err(DefaultError::new("".to_string())));
124
125 let git = GitImpl::new(&shell_mock);
126 assert!(git.has_changed(¬e));
127 }
128
129 #[test]
130 fn push() {
131 let mut shell_mock = MockShell::new();
132 let exp_command = "git push";
133 shell_mock
134 .expect_execute_interactive_in_repo()
135 .times(1)
136 .withf(move |c| c == exp_command)
137 .returning(|_| Ok(CommandOutput::default()));
138
139 let git = GitImpl::new(&shell_mock);
140 git.push().unwrap();
141 }
142
143 #[test]
144 fn pull() {
145 let mut shell_mock = MockShell::new();
146 let exp_command = "git pull";
147 shell_mock
148 .expect_execute_interactive_in_repo()
149 .times(1)
150 .withf(move |c| c == exp_command)
151 .returning(|_| Ok(CommandOutput::default()));
152
153 let git = GitImpl::new(&shell_mock);
154 git.pull().unwrap();
155 }
156}