use serde_json::Value as JsonValue;
#[derive(Debug)]
pub struct GitHubApiError {
pub status: u16,
pub response_text: String,
}
impl GitHubApiError {
pub fn response_message(&self) -> Option<String> {
let body: JsonValue = serde_json::from_str(&self.response_text).ok()?;
body.get("message")
.and_then(JsonValue::as_str)
.map(str::to_owned)
}
pub fn message(&self) -> String {
let mut text = self.response_text.clone();
text.truncate(300);
format!("GitHub API {}: {}", self.status, text)
}
}
pub enum GitHubError {
Api(GitHubApiError),
Other(String),
}
impl GitHubError {
pub(super) fn other(err: impl std::fmt::Display) -> Self {
Self::Other(err.to_string())
}
}
pub type GitHubResult<T> = std::result::Result<T, GitHubError>;
pub fn github_error_message(error: &GitHubError, action: &str) -> String {
match error {
GitHubError::Api(api) => {
if api.status == 403
&& api.response_message().as_deref()
== Some("Resource not accessible by integration")
{
return format!(
"{action} failed because the GitHub credential cannot write to this repository. \
Use GitHub OAuth App credentials, make sure the user has write access to the \
repository, then log out and sign in again so the token is authorized with the \
repo scope."
);
}
api.message()
}
GitHubError::Other(message) => message.clone(),
}
}