1use std::fmt::Display;
2use std::process::Command;
3use std::thread::sleep;
4use std::time::Duration;
5
6pub fn wait_for_change(
7 url: String,
8 branch: String,
9 interval: Duration,
10 local_path: Option<String>,
11) -> Result<(), GitWatcherError> {
12 let last_commit_hash = match local_path {
13 Some(path) => match get_last_local_commit_hash(&path, &branch) {
14 Some(commit) => commit,
15 None => {
16 return Err(GitWatcherError::LocalHashCheckError);
17 }
18 },
19 None => match get_last_remote_commit_hash(&url, &branch) {
20 Some(commit) => commit,
21 None => {
22 return Err(GitWatcherError::RemoteHashCheckError);
23 }
24 },
25 };
26 loop {
27 let current_commit_hash = match get_last_remote_commit_hash(&url, &branch) {
28 Some(commit) => commit,
29 None => {
30 return Err(GitWatcherError::LoopError);
31 }
32 };
33 if current_commit_hash != last_commit_hash {
34 return Ok(());
35 }
36 sleep(interval);
37 }
38}
39
40#[derive(Debug)]
41pub enum GitWatcherError {
42 LocalHashCheckError,
43 RemoteHashCheckError,
44 LoopError,
45}
46
47impl Display for GitWatcherError {
48 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
49 match self {
50 GitWatcherError::LocalHashCheckError => write!(f, "Local hash check error"),
51 GitWatcherError::RemoteHashCheckError => write!(f, "Remote hash check error"),
52 GitWatcherError::LoopError => write!(f, "Loop error"),
53 }
54 }
55}
56
57pub fn get_last_remote_commit_hash(url: &String, branch: &String) -> Option<String> {
58 let output = Command::new("git")
59 .arg("ls-remote")
60 .arg(url)
61 .arg(branch)
62 .output();
63 match output {
64 Ok(output) => get_head_hash(output),
65 Err(_) => {
66 println!("There was an error while running git ls-remote.");
67 println!("This might happen if the internet connection is down or the URL is invalid.");
68 None
69 }
70 }
71}
72
73pub fn get_last_local_commit_hash(path: &String, branch: &String) -> Option<String> {
74 let output = Command::new("git")
75 .arg("rev-parse")
76 .arg(branch)
77 .current_dir(path)
78 .output();
79 match output {
80 Ok(output) => get_head_hash(output),
81 Err(_) => {
82 println!("There was an error while running git rev-parse on the local repository.");
83 None
84 }
85 }
86}
87
88fn get_head_hash(output: std::process::Output) -> Option<String> {
89 let output = String::from_utf8(output.stdout).expect("utf8");
90 if output.is_empty() {
91 println!("There was an error while parsing the git output.");
92 None
93 } else {
94 let mut lines = output.lines();
95 let line = lines.next()?;
96 let hash = line.split('\t').next()?;
97 return Some(hash.to_string());
98 }
99}