use crate::config::Config;
use crate::error::{FnoxError, Result};
use crate::providers::ProviderConfig;
use demand::Confirm;
use std::process::Command;
pub fn prompt_and_run_auth(
config: &Config,
provider_config: &ProviderConfig,
provider_name: &str,
error: &FnoxError,
) -> Result<bool> {
if !error.is_auth_error() {
return Ok(false);
}
if !config.should_prompt_auth() {
return Ok(false);
}
let Some(auth_command) = provider_config.default_auth_command() else {
return Ok(false);
};
eprintln!(
"Authentication failed for provider '{}': {}",
provider_name, error
);
let user_confirmed = Confirm::new(format!("Run `{}` to authenticate?", auth_command))
.affirmative("Yes")
.negative("No")
.run()
.map_err(|e| FnoxError::Provider(format!("Failed to show prompt: {}", e)))?;
if !user_confirmed {
return Ok(false);
}
eprintln!("Running: {}", auth_command);
let status = if cfg!(target_os = "windows") {
Command::new("cmd").args(["/C", auth_command]).status()
} else {
Command::new("sh").args(["-c", auth_command]).status()
};
match status {
Ok(exit_status) if exit_status.success() => {
eprintln!("Authentication successful, retrying...");
Ok(true)
}
Ok(exit_status) => Err(FnoxError::Provider(format!(
"Auth command failed with exit code: {}",
exit_status.code().unwrap_or(-1)
))),
Err(e) => Err(FnoxError::Provider(format!(
"Failed to run auth command: {}",
e
))),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::providers::OptionStringOrSecretRef;
fn provider_with_auth_command() -> ProviderConfig {
ProviderConfig::OnePassword {
vault: OptionStringOrSecretRef::literal("default"),
account: OptionStringOrSecretRef::none(),
token: OptionStringOrSecretRef::none(),
auth_command: None,
}
}
#[test]
fn non_auth_error_skips_prompt() {
let config = Config::new();
let provider_config = provider_with_auth_command();
let error = FnoxError::ProviderSecretNotFound {
provider: "test".to_string(),
secret: "MY_SECRET".to_string(),
hint: "check".to_string(),
url: "https://example.com".to_string(),
};
let result = prompt_and_run_auth(&config, &provider_config, "1password", &error);
assert_eq!(result.unwrap(), false);
}
#[test]
fn cli_failed_error_skips_prompt() {
let config = Config::new();
let provider_config = provider_with_auth_command();
let error = FnoxError::ProviderCliFailed {
provider: "test".to_string(),
details: "field does not exist".to_string(),
hint: "check".to_string(),
url: "https://example.com".to_string(),
};
let result = prompt_and_run_auth(&config, &provider_config, "1password", &error);
assert_eq!(result.unwrap(), false);
}
}