use std::{fs, io, path::Path, thread, time};
const SYNC_WAIT: u64 = 30;
pub fn start(root: &str) -> io::Result<()> {
if !is_git_repo(root) {
return Err(io::Error::new(
io::ErrorKind::NotFound,
format!("{} is not a git repo", root),
));
}
println!("~> running sync service");
let root = root.to_string();
thread::spawn(move || sync_periodically(&root));
Ok(())
}
fn is_git_repo(root: &str) -> bool {
let dir = format!("{}.git", root);
let path = Path::new(&dir);
if let Ok(file) = fs::File::open(path) {
if let Ok(meta) = file.metadata() {
return meta.is_dir();
}
}
false
}
fn sync_periodically(root: &str) -> io::Result<()> {
let period = time::Duration::from_millis(SYNC_WAIT * 1000);
loop {
save_changes(root)?;
sync_changes(root)?;
thread::sleep(period);
}
}
macro_rules! git {
($root:expr, $($arg:expr),+) => {
git($root, &[$($arg),+])
};
}
fn save_changes(root: &str) -> io::Result<bool> {
let pending = git!(root, "status", "-s")?;
if pending.is_empty() {
return Ok(false);
}
let changes = pending
.split('\n')
.map(|file| if file.len() > 3 { &file[3..] } else { file }.trim())
.filter(|f| !f.is_empty())
.collect::<Vec<_>>();
let status = changes.join(if changes.len() == 1 { "" } else { ", " });
println!("~> saving changes: {}", status);
git!(root, "add", ".")?;
git!(root, "commit", "-am", &status)?;
Ok(true)
}
fn sync_changes(root: &str) -> io::Result<bool> {
println!("~> syncing changes");
git!(root, "pull", "origin", "master")?;
git!(root, "push", "origin", "master")?;
Ok(true)
}
fn git(root: &str, args: &[&str]) -> io::Result<String> {
shell!(
"git --git-dir {root}.git --work-tree {root} {args}",
root = root,
args = args.join(" ")
)
}