1use std::fmt;
10use std::process::Command;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum Source {
15 GhLogin,
17 Environment,
19}
20
21impl fmt::Display for Source {
22 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23 f.write_str(match self {
24 Self::GhLogin => "the GitHub CLI's login (gh auth token)",
25 Self::Environment => "GITHUB_TOKEN",
26 })
27 }
28}
29
30#[derive(Clone)]
32pub struct Token {
33 value: String,
34 pub source: Source,
35}
36
37impl Token {
38 pub fn value(&self) -> &str {
39 &self.value
40 }
41}
42
43impl fmt::Debug for Token {
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 f.debug_struct("Token").field("source", &self.source).finish_non_exhaustive()
46 }
47}
48
49#[derive(Debug, Clone, PartialEq, Eq)]
51pub struct NoToken {
52 pub gh: String,
53}
54
55impl fmt::Display for NoToken {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 write!(
58 f,
59 "no GitHub token: {}, and GITHUB_TOKEN is not set. Log in with `gh auth login` \
60 (with the `project` scope for boards: `gh auth refresh -s project`), or set GITHUB_TOKEN",
61 self.gh
62 )
63 }
64}
65
66impl std::error::Error for NoToken {}
67
68pub fn resolve() -> Result<Token, NoToken> {
70 let gh = match Command::new("gh").args(["auth", "token"]).output() {
71 Ok(out) if out.status.success() => {
72 let value = String::from_utf8_lossy(&out.stdout).trim().to_string();
73 if !value.is_empty() {
74 return Ok(Token {
75 value,
76 source: Source::GhLogin,
77 });
78 }
79 "the GitHub CLI answered with no token".to_string()
80 }
81 Ok(out) => format!(
82 "the GitHub CLI has no login ({})",
83 String::from_utf8_lossy(&out.stderr).trim()
84 ),
85 Err(error) if error.kind() == std::io::ErrorKind::NotFound => {
86 "the GitHub CLI (gh) is not installed".to_string()
87 }
88 Err(error) => format!("the GitHub CLI could not be run ({error})"),
89 };
90 match std::env::var("GITHUB_TOKEN") {
91 Ok(value) if !value.trim().is_empty() => Ok(Token {
92 value: value.trim().to_string(),
93 source: Source::Environment,
94 }),
95 _ => Err(NoToken { gh }),
96 }
97}