use std::process::{Command, Stdio};
use anyhow::{Context, Result};
pub enum SwitchResult {
Ok,
Failed(String),
NotInstalled,
}
pub fn switch(gh_user: &str) -> Result<SwitchResult> {
let output = Command::new("gh")
.args(["auth", "switch", "--user", gh_user])
.output();
match output {
Ok(out) if out.status.success() => Ok(SwitchResult::Ok),
Ok(out) => Ok(SwitchResult::Failed(
String::from_utf8_lossy(&out.stderr).trim().to_string(),
)),
Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(SwitchResult::NotInstalled),
Err(e) => Err(e.into()),
}
}
pub fn login() -> Result<bool> {
let status = Command::new("gh")
.args(["auth", "login", "--hostname", "github.com"])
.stdin(Stdio::inherit())
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.status()
.context("failed to run `gh auth login`")?;
Ok(status.success())
}
pub fn current_user() -> Option<String> {
let output = Command::new("gh")
.args(["auth", "status", "--active"])
.output()
.ok()?;
let text = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
parse_active_user(&text).or_else(|| parse_active_user(&stderr))
}
fn parse_active_user(text: &str) -> Option<String> {
for line in text.lines() {
if let Some(idx) = line.find("account ") {
let rest = &line[idx + "account ".len()..];
let user = rest.split_whitespace().next()?;
if !user.is_empty() {
return Some(user.to_string());
}
}
}
None
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_active_user_from_status_line() {
let text = "github.com\n ✓ Logged in to github.com account octocat (keyring)\n - Active account: true";
assert_eq!(parse_active_user(text), Some("octocat".to_string()));
}
#[test]
fn returns_none_when_no_account_line() {
assert_eq!(parse_active_user("You are not logged in"), None);
}
}