geoit 0.0.2

Exact geometric algebra with governed multivectors
Documentation
//! Unified error type for the geoit crate.
//!
//! Re-exports all sub-error types and provides a unified [`Error`] enum
//! with `From` impls for convenient `?` propagation.

use crate::algebra::inverse::InverseError;
use crate::governance::composition::EmbeddingError;
use crate::governance::construction::ConstructionError;
use crate::governance::expr::EvalError;
use crate::governance::groebner::GroebnerError;
use crate::governance::morphism::MorphismError;
use crate::governance::reading::ExtractionError;
use crate::governance::rule::TransformError;
use crate::governance::GovernanceError;

/// Signature construction error.
#[derive(Clone, Debug)]
pub enum SignatureError {
    /// i + d + h exceeds 64.
    TooManyGenerators { i: u8, d: u8, h: u8, total: u16 },
}

impl std::fmt::Display for SignatureError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            SignatureError::TooManyGenerators { i, d, h, total } => write!(
                f,
                "signature Cl({},{},{}) has {} generators (max 64)",
                i, d, h, total
            ),
        }
    }
}
impl std::error::Error for SignatureError {}

/// Name lookup error.
#[derive(Clone, Debug)]
pub enum NotFoundError {
    /// Class name not found in governance.
    Class(String),
    /// Construction name not found in governance.
    Construction(String),
    /// Derived generator name not found in algebra.
    DerivedGen(String),
}

impl std::fmt::Display for NotFoundError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            NotFoundError::Class(name) => write!(f, "no class named '{}'", name),
            NotFoundError::Construction(name) => write!(f, "no construction named '{}'", name),
            NotFoundError::DerivedGen(name) => write!(f, "no derived generator named '{}'", name),
        }
    }
}
impl std::error::Error for NotFoundError {}

/// Unified error type wrapping all geoit sub-errors.
///
/// Every fallible operation in geoit returns `Result<T, Error>`.
/// Use the `?` operator to propagate errors.
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum Error {
    /// Signature construction failed.
    Signature(SignatureError),
    /// Governance constraint violation.
    Governance(Box<GovernanceError>),
    /// Mv construction failed.
    Construction(ConstructionError),
    /// Embedding construction failed.
    Embedding(EmbeddingError),
    /// Morphism construction failed.
    Morphism(Box<MorphismError>),
    /// Parameter extraction failed.
    Extraction(ExtractionError),
    /// Mv inversion failed.
    Inverse(InverseError),
    /// Name lookup failed.
    NotFound(NotFoundError),
    /// Expression validation failed.
    Eval(Vec<EvalError>),
    /// Gröbner basis computation did not converge.
    Groebner(GroebnerError),
    /// Transform rule application failed.
    Transform(TransformError),
}

impl std::fmt::Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Error::Signature(e) => write!(f, "{}", e),
            Error::Governance(e) => write!(f, "governance: {}", e),
            Error::Construction(e) => write!(f, "construction: {}", e),
            Error::Embedding(e) => write!(f, "embedding: {}", e),
            Error::Morphism(e) => write!(f, "morphism: {}", e),
            Error::Extraction(e) => write!(f, "extraction: {}", e),
            Error::Inverse(e) => write!(f, "inverse: {}", e),
            Error::NotFound(e) => write!(f, "{}", e),
            Error::Eval(errors) => {
                write!(f, "expression validation failed: ")?;
                for (i, e) in errors.iter().enumerate() {
                    if i > 0 {
                        write!(f, "; ")?;
                    }
                    write!(f, "{}", e)?;
                }
                Ok(())
            }
            Error::Groebner(e) => write!(f, "groebner: {}", e),
            Error::Transform(e) => write!(f, "transform: {}", e),
        }
    }
}

impl std::error::Error for Error {}

impl From<SignatureError> for Error {
    fn from(e: SignatureError) -> Self {
        Error::Signature(e)
    }
}
impl From<GovernanceError> for Error {
    fn from(e: GovernanceError) -> Self {
        Error::Governance(Box::new(e))
    }
}
impl From<ConstructionError> for Error {
    fn from(e: ConstructionError) -> Self {
        Error::Construction(e)
    }
}
impl From<EmbeddingError> for Error {
    fn from(e: EmbeddingError) -> Self {
        Error::Embedding(e)
    }
}
impl From<MorphismError> for Error {
    fn from(e: MorphismError) -> Self {
        Error::Morphism(Box::new(e))
    }
}
impl From<ExtractionError> for Error {
    fn from(e: ExtractionError) -> Self {
        Error::Extraction(e)
    }
}
impl From<InverseError> for Error {
    fn from(e: InverseError) -> Self {
        Error::Inverse(e)
    }
}
impl From<NotFoundError> for Error {
    fn from(e: NotFoundError) -> Self {
        Error::NotFound(e)
    }
}
impl From<Vec<EvalError>> for Error {
    fn from(e: Vec<EvalError>) -> Self {
        Error::Eval(e)
    }
}
impl From<GroebnerError> for Error {
    fn from(e: GroebnerError) -> Self {
        Error::Groebner(e)
    }
}
impl From<TransformError> for Error {
    fn from(e: TransformError) -> Self {
        Error::Transform(e)
    }
}

// std::error::Error impls for sub-error types that don't have them yet
impl std::error::Error for GovernanceError {}
impl std::error::Error for ConstructionError {}
impl std::error::Error for MorphismError {}
impl std::error::Error for EmbeddingError {}
impl std::error::Error for ExtractionError {}
impl std::error::Error for InverseError {}
impl std::error::Error for EvalError {}
impl std::error::Error for GroebnerError {}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn error_display() {
        let e = Error::Signature(SignatureError::TooManyGenerators {
            i: 30,
            d: 20,
            h: 20,
            total: 70,
        });
        assert!(format!("{}", e).contains("70"));
    }

    #[test]
    fn error_from_not_found() {
        let e: Error = NotFoundError::Class("Foo".into()).into();
        assert!(format!("{}", e).contains("Foo"));
    }

    #[test]
    fn error_from_governance() {
        let e: Error = GovernanceError::ClassOutOfRange { index: 5, len: 2 }.into();
        assert!(matches!(e, Error::Governance(_)));
    }

    #[test]
    fn error_question_mark() {
        fn inner() -> Result<(), Error> {
            Err(NotFoundError::Class("X".into()))?
        }
        assert!(inner().is_err());
    }
}