use std::path::PathBuf;
use omnifuse_core::ErrorKind;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum GitError {
#[error("backend not initialized")]
NotInitialized,
#[error("not a git repository: {path}")]
InvalidRepository {
path: PathBuf
},
#[error("nothing to commit")]
NothingToCommit,
#[error("no files to commit")]
NoFilesToCommit,
#[error("network unavailable: {message}")]
NetworkUnavailable {
message: String
},
#[error("{count} file(s) in conflict", count = .files.len())]
Conflict {
files: Vec<PathBuf>
},
#[error("push rejected after {retries} attempts")]
PushRejected {
retries: u32
},
#[error("{op} failed: {stderr}")]
CommandFailed {
op: &'static str,
stderr: String
}
}
#[must_use]
pub fn classify_git_error(error: &anyhow::Error) -> Option<ErrorKind> {
match error.downcast_ref::<GitError>() {
Some(GitError::NetworkUnavailable { .. }) => Some(ErrorKind::Offline),
Some(GitError::Conflict { .. } | GitError::PushRejected { .. }) => Some(ErrorKind::Conflict),
Some(GitError::InvalidRepository { .. }) => Some(ErrorKind::InvalidConfig),
Some(GitError::CommandFailed { .. }) => Some(ErrorKind::BackendCommandFailed),
Some(GitError::NotInitialized) => Some(ErrorKind::Internal),
Some(GitError::NothingToCommit | GitError::NoFilesToCommit) | None => None
}
}
#[must_use]
pub fn is_nothing_to_commit(error: &anyhow::Error) -> bool {
matches!(error.downcast_ref::<GitError>(), Some(GitError::NothingToCommit))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_classify_git_error() {
let offline: anyhow::Error = GitError::NetworkUnavailable {
message: "dns".to_string()
}
.into();
let conflict: anyhow::Error = GitError::Conflict {
files: vec!["README.md".into()]
}
.into();
let nothing: anyhow::Error = GitError::NothingToCommit.into();
assert_eq!(classify_git_error(&offline), Some(ErrorKind::Offline));
assert_eq!(classify_git_error(&conflict), Some(ErrorKind::Conflict));
assert_eq!(classify_git_error(¬hing), None);
assert!(is_nothing_to_commit(¬hing));
}
}