use std::process::Command;
#[cfg(test)]
use secrecy::ExposeSecret;
use secrecy::SecretString;
use crate::error::{Error, Result};
#[derive(Debug, Clone)]
pub enum Auth {
GhCli,
EnvVar(String),
Token(SecretString),
}
impl Auth {
#[must_use]
pub fn auto() -> Self {
if std::env::var("GITHUB_TOKEN").is_ok() {
Self::EnvVar("GITHUB_TOKEN".into())
} else {
Self::GhCli
}
}
pub fn resolve(&self) -> Result<SecretString> {
match self {
Self::GhCli => get_gh_token(),
Self::EnvVar(var) => std::env::var(var)
.map(SecretString::from)
.map_err(|_| Error::NoToken),
Self::Token(t) => Ok(t.clone()),
}
}
}
impl Default for Auth {
fn default() -> Self {
Self::auto()
}
}
fn get_gh_token() -> Result<SecretString> {
let output = Command::new("gh").args(["auth", "token"]).output()?;
if !output.status.success() {
return Err(Error::NoToken);
}
let token = String::from_utf8_lossy(&output.stdout).trim().to_string();
if token.is_empty() {
return Err(Error::NoToken);
}
Ok(SecretString::from(token))
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
#[test]
fn test_auth_auto_prefers_env() {
let _auth = Auth::auto();
}
#[test]
fn test_token_auth() {
let auth = Auth::Token(SecretString::from("test_token"));
assert_eq!(auth.resolve().unwrap().expose_secret(), "test_token");
}
#[test]
fn test_env_var_auth_missing() {
let auth = Auth::EnvVar("RUNG_TEST_NONEXISTENT_VAR_9a8b7c6d5e4f".into());
assert!(auth.resolve().is_err());
}
#[test]
fn test_auth_default() {
let auth = Auth::default();
match auth {
Auth::GhCli | Auth::EnvVar(_) => {}
Auth::Token(_) => panic!("Default should not return Token"),
}
}
}