greentic-dev 1.1.0-dev.25480057307

Developer CLI and local tooling for Greentic flows, packs, and components
Documentation
use std::io::{self, IsTerminal, Write};

use anyhow::{Result, bail};

use crate::i18n;

pub fn ensure_execute_allowed(
    summary: &str,
    yes: bool,
    non_interactive: bool,
    locale: &str,
) -> Result<()> {
    let interactive = io::stdin().is_terminal() && io::stdout().is_terminal();
    ensure_execute_allowed_with(summary, yes, non_interactive, locale, interactive, || {
        let mut input = String::new();
        io::stdin().read_line(&mut input)?;
        Ok(input)
    })
}

fn ensure_execute_allowed_with<F>(
    summary: &str,
    yes: bool,
    non_interactive: bool,
    locale: &str,
    interactive: bool,
    read_input: F,
) -> Result<()>
where
    F: FnOnce() -> Result<String>,
{
    if yes {
        return Ok(());
    }

    if !interactive {
        if non_interactive {
            return Ok(());
        }
        bail!(
            "{}",
            i18n::t(locale, "runtime.wizard.confirm.error.non_interactive")
        );
    }

    eprintln!("{summary}");
    eprint!("{}", i18n::t(locale, "runtime.wizard.confirm.prompt"));
    io::stderr().flush()?;
    let input = read_input()?;
    let accepted = matches!(input.trim().to_ascii_lowercase().as_str(), "y" | "yes");
    if accepted {
        Ok(())
    } else {
        bail!(
            "{}",
            i18n::t(locale, "runtime.wizard.confirm.error.canceled")
        )
    }
}

#[cfg(test)]
mod tests {
    use super::ensure_execute_allowed_with;

    #[test]
    fn yes_flag_bypasses_confirmation() {
        ensure_execute_allowed_with("summary", true, false, "en", true, || {
            panic!("should not prompt")
        })
        .unwrap();
    }

    #[test]
    fn non_interactive_mode_allows_execution_without_tty() {
        ensure_execute_allowed_with("summary", false, true, "en", false, || {
            panic!("should not prompt")
        })
        .unwrap();
    }

    #[test]
    fn non_interactive_without_opt_in_is_rejected() {
        let err = ensure_execute_allowed_with("summary", false, false, "en", false, || {
            panic!("should not prompt")
        })
        .unwrap_err();
        assert!(err.to_string().contains("non-interactive"));
    }

    #[test]
    fn interactive_decline_is_rejected() {
        let err = ensure_execute_allowed_with("summary", false, false, "en", true, || {
            Ok("n\n".to_string())
        })
        .unwrap_err();
        assert!(err.to_string().contains("canceled"));
    }

    #[test]
    fn interactive_accept_is_allowed() {
        ensure_execute_allowed_with("summary", false, false, "en", true, || {
            Ok("yes\n".to_string())
        })
        .unwrap();
    }
}