Skip to main content

rung_github/
auth.rs

1//! Authentication handling for GitHub API.
2
3use std::process::Command;
4
5use crate::error::{Error, Result};
6
7/// Authentication method for GitHub API.
8#[derive(Debug, Clone)]
9pub enum Auth {
10    /// Use token from gh CLI.
11    GhCli,
12
13    /// Use token from environment variable.
14    EnvVar(String),
15
16    /// Use a specific token.
17    Token(String),
18}
19
20impl Auth {
21    /// Create auth from the first available method.
22    ///
23    /// Tries in order: `GITHUB_TOKEN` env var, gh CLI.
24    #[must_use]
25    pub fn auto() -> Self {
26        if std::env::var("GITHUB_TOKEN").is_ok() {
27            Self::EnvVar("GITHUB_TOKEN".into())
28        } else {
29            Self::GhCli
30        }
31    }
32
33    /// Resolve the authentication to a token string.
34    ///
35    /// # Errors
36    /// Returns error if token cannot be obtained.
37    pub fn resolve(&self) -> Result<String> {
38        match self {
39            Self::GhCli => get_gh_token(),
40            Self::EnvVar(var) => std::env::var(var).map_err(|_| Error::NoToken),
41            Self::Token(t) => Ok(t.clone()),
42        }
43    }
44}
45
46impl Default for Auth {
47    fn default() -> Self {
48        Self::auto()
49    }
50}
51
52/// Get GitHub token from gh CLI.
53fn get_gh_token() -> Result<String> {
54    let output = Command::new("gh").args(["auth", "token"]).output()?;
55
56    if !output.status.success() {
57        return Err(Error::NoToken);
58    }
59
60    let token = String::from_utf8_lossy(&output.stdout).trim().to_string();
61
62    if token.is_empty() {
63        return Err(Error::NoToken);
64    }
65
66    Ok(token)
67}
68
69#[cfg(test)]
70#[allow(clippy::unwrap_used)]
71mod tests {
72    use super::*;
73
74    #[test]
75    fn test_auth_auto_prefers_env() {
76        // This test depends on environment, so just ensure it doesn't panic
77        let _auth = Auth::auto();
78    }
79
80    #[test]
81    fn test_token_auth() {
82        let auth = Auth::Token("test_token".into());
83        assert_eq!(auth.resolve().unwrap(), "test_token");
84    }
85}