use std::io;
use std::io::ErrorKind;
use std::process::Output;
#[derive(Clone)]
pub(super) enum GithubAuth {
Authenticated(String),
Unauthenticated,
NotInstalled,
}
impl GithubAuth {
pub(super) fn classify(output: io::Result<Output>) -> Self {
match output {
Ok(output) if output.status.success() => String::from_utf8(output.stdout)
.map_or(Self::Unauthenticated, |token| {
Self::Authenticated(token.trim().to_string())
}),
Err(error) if error.kind() == ErrorKind::NotFound => Self::NotInstalled,
Ok(_) | Err(_) => Self::Unauthenticated,
}
}
pub(super) const fn token(&self) -> Option<&str> {
match self {
Self::Authenticated(token) => Some(token.as_str()),
Self::Unauthenticated | Self::NotInstalled => None,
}
}
pub(super) const fn gap(&self) -> Option<GithubAuthGap> {
match self {
Self::Authenticated(_) => None,
Self::Unauthenticated => Some(GithubAuthGap::Unauthenticated),
Self::NotInstalled => Some(GithubAuthGap::NotInstalled),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum GithubAuthGap {
NotInstalled,
Unauthenticated,
}
#[cfg(test)]
#[cfg(unix)]
mod classify {
use std::io;
use std::io::ErrorKind;
use std::os::unix::process::ExitStatusExt;
use std::process::ExitStatus;
use std::process::Output;
use super::GithubAuth;
fn gh_output(raw_wait_status: i32, stdout: &[u8]) -> Output {
Output {
status: ExitStatus::from_raw(raw_wait_status),
stdout: stdout.to_vec(),
stderr: Vec::new(),
}
}
#[test]
fn success_exit_with_token_is_authenticated() {
let github_auth = GithubAuth::classify(Ok(gh_output(0, b" gho_abc123\n")));
assert!(matches!(github_auth, GithubAuth::Authenticated(token) if token == "gho_abc123"));
}
#[test]
fn success_exit_with_invalid_utf8_is_unauthenticated() {
let github_auth = GithubAuth::classify(Ok(gh_output(0, &[0xff, 0xfe])));
assert!(matches!(github_auth, GithubAuth::Unauthenticated));
}
#[test]
fn nonsuccess_exit_is_unauthenticated() {
let github_auth = GithubAuth::classify(Ok(gh_output(1 << 8, b"not logged in")));
assert!(matches!(github_auth, GithubAuth::Unauthenticated));
}
#[test]
fn missing_binary_is_not_installed() {
let github_auth = GithubAuth::classify(Err(io::Error::from(ErrorKind::NotFound)));
assert!(matches!(github_auth, GithubAuth::NotInstalled));
}
#[test]
fn other_spawn_error_is_unauthenticated() {
let github_auth = GithubAuth::classify(Err(io::Error::from(ErrorKind::PermissionDenied)));
assert!(matches!(github_auth, GithubAuth::Unauthenticated));
}
}