nedots 0.1.3

A tool to manage configuration files/(ne)dots.
use crate::errors::Error;

#[derive(Debug, clap::Parser)]
pub(crate) struct SyncCmd {
    #[arg(short, long)]
    /// Gather dots before syncing.
    gather: bool,
}

impl super::Command for SyncCmd {
    fn run(
        &self,
        _: &super::nedots::RootCmd,
        config: &crate::config::Config,
    ) -> anyhow::Result<()> {
        if self.gather {
            for source in &config.sources {
                super::gather::gather_file(source, &config.get_dst_from_src(source)?)?;
            }
        }

        let cmd = "git";
        let initial_args = ["-C", &config.root.display().to_string()];
        let spinner = start_spinner();

        let mut args = Vec::new();
        args.extend_from_slice(&initial_args);
        args.extend_from_slice(&["submodule", "foreach", "git", "pull"]);
        run_cmd(cmd, &args, &spinner)?;

        args = Vec::new();
        args.extend_from_slice(&initial_args);
        args.extend_from_slice(&["add", "."]);
        run_cmd(cmd, &args, &spinner)?;

        args = Vec::new();
        args.extend_from_slice(&initial_args);

        let datetime = format!("Latest {}", chrono::offset::Local::now());
        args.extend_from_slice(&["commit", "-m", &datetime]);

        let res = run_cmd(cmd, &args, &spinner);
        if res.is_err() {
            log::trace!("expected `git commit` to error and it did, moving on");
        }

        args = Vec::new();
        args.extend_from_slice(&initial_args);
        args.extend_from_slice(&["pull"]);
        run_cmd(cmd, &args, &spinner)?;

        args = Vec::new();
        args.extend_from_slice(&initial_args);
        args.extend_from_slice(&["push"]);
        run_cmd(cmd, &args, &spinner)?;

        spinner.finish_and_clear();
        log::info!("✅ Synced");

        Ok(())
    }
}

fn start_spinner() -> indicatif::ProgressBar {
    let tick_rate = std::time::Duration::from_millis(500);
    let spinner = indicatif::ProgressBar::new_spinner();
    spinner.enable_steady_tick(tick_rate);

    spinner
}

fn run_cmd(prog: &str, args: &[&str], spinner: &indicatif::ProgressBar) -> anyhow::Result<()> {
    spinner.set_message(format!("{} {}", prog, args.join(" ")));

    let output = std::process::Command::new(prog).args(args).output()?;
    match output.status.success() {
        true => Ok(()),
        false => {
            if !output.stderr.is_empty() {
                eprintln!("{}", String::from_utf8(output.stderr)?);
            }

            Err(Error::Command(prog.to_string()).into())
        }
    }
}