tapes-v2 0.1.3-beta

Swap your whole rice in one command. Eject it with one more. Atomic dotfiles switching for Linux.
use anyhow::{anyhow, bail, Result};
use log::error;
use std::{env, process::Command};

use crate::toml::TapeManifest;

pub fn run_hooks(hooks: Vec<String>, quiet: bool) -> Result<(), anyhow::Error> {
    if hooks.is_empty() {
        return Ok(());
    }

    let mut failed = false;

    for hook in hooks {
        let hook = hook.trim();

        if !quiet {
            println!(" - '{}'", hook);
        }

        if let Err(err) = run_command(hook) {
            error!("Hook failed: '{}': {}", hook, err);
            failed = true;
        }
    }

    if failed {
        bail!("One or more hooks failed");
    }

    Ok(())
}

pub fn run_command(command: &str) -> Result<(), anyhow::Error> {
    let shell = get_user_preferred_shell();

    let status = Command::new(shell).arg("-c").arg(command).status()?;

    if !status.success() {
        return Err(anyhow!("Command failed with exit status: {}", status));
    }

    Ok(())
}

fn get_user_preferred_shell() -> String {
    env::var("SHELL").unwrap_or("sh".to_string())
}

fn run_lifecycle_hooks(
    hooks: Option<Vec<String>>,
    no_hooks: bool,
    quiet: bool,
    stage: &str,
) -> Result<(), anyhow::Error> {
    if no_hooks {
        return Ok(());
    }

    let Some(hooks) = hooks else {
        return Ok(());
    };

    if hooks.is_empty() {
        return Ok(());
    }

    println!("Running hooks");

    if let Err(err) = run_hooks(hooks, quiet) {
        error!("{} hooks completed with warnings/errors: {}", stage, err);
    }

    Ok(())
}

pub fn run_eject_hooks(
    manifest: TapeManifest,
    no_hooks: bool,
    quiet: bool,
) -> Result<(), anyhow::Error> {
    let hooks = manifest.hooks.map(|h| h.eject);
    run_lifecycle_hooks(hooks, no_hooks, quiet, "Eject")
}

pub fn run_insert_hooks(
    manifest: TapeManifest,
    no_hooks: bool,
    quiet: bool,
) -> Result<(), anyhow::Error> {
    let hooks = manifest.hooks.map(|h| h.insert);
    run_lifecycle_hooks(hooks, no_hooks, quiet, "Insert")
}

pub fn run_install_hooks(
    manifest: TapeManifest,
    no_hooks: bool,
    // quiet: bool,
) -> Result<(), anyhow::Error> {
    let hooks = manifest.hooks.map(|h| h.eject);
    run_lifecycle_hooks(hooks, no_hooks, false, "Install")
}