weavatrix-git 0.2.0

Dependency-free, evidence-carrying Git repository reader
Documentation
use std::{fmt, io};

pub type Result<T> = std::result::Result<T, GitError>;

#[derive(Debug)]
pub enum GitError {
    Io(io::Error),
    NotRepository(String),
    InvalidFormat(String),
    NotFound(String),
    Unsupported(String),
    LimitExceeded {
        resource: &'static str,
        limit: usize,
    },
}

impl fmt::Display for GitError {
    fn fmt(&self, output: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Io(error) => write!(output, "I/O error: {error}"),
            Self::NotRepository(message) => write!(output, "not a Git repository: {message}"),
            Self::InvalidFormat(message) => write!(output, "invalid Git data: {message}"),
            Self::NotFound(message) => write!(output, "Git object not found: {message}"),
            Self::Unsupported(message) => write!(output, "unsupported Git feature: {message}"),
            Self::LimitExceeded { resource, limit } => {
                write!(output, "{resource} exceeds configured limit {limit}")
            }
        }
    }
}

impl std::error::Error for GitError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match self {
            Self::Io(error) => Some(error),
            _ => None,
        }
    }
}

impl From<io::Error> for GitError {
    fn from(error: io::Error) -> Self {
        Self::Io(error)
    }
}

pub(crate) fn invalid(message: impl Into<String>) -> GitError {
    GitError::InvalidFormat(message.into())
}

#[cfg(test)]
mod tests {
    use std::{error::Error, io};

    use super::GitError;

    #[test]
    fn formats_every_error_variant() {
        let errors = [
            GitError::Io(io::Error::other("disk")),
            GitError::NotRepository("path".to_owned()),
            GitError::InvalidFormat("bytes".to_owned()),
            GitError::NotFound("object".to_owned()),
            GitError::Unsupported("feature".to_owned()),
            GitError::LimitExceeded {
                resource: "depth",
                limit: 4,
            },
        ];
        for error in errors {
            assert!(!error.to_string().is_empty());
        }
        assert!(GitError::Io(io::Error::other("disk")).source().is_some());
        assert!(GitError::NotFound("x".to_owned()).source().is_none());
    }
}