use comp_cat_rs::collapse::free_category::FreeCategoryError;
use crate::shape::Shape;
#[derive(Debug)]
pub enum CsaError {
TreeSizeZero,
BitWidthMismatch {
expected: usize,
actual: usize,
},
ShapeMismatch {
left: Shape,
right: Shape,
},
InvalidGrouping {
count: usize,
},
UpstreamGraph(FreeCategoryError),
#[cfg(feature = "hdl-cat-gates")]
HdlCat(hdl_cat::Error),
}
impl From<FreeCategoryError> for CsaError {
fn from(e: FreeCategoryError) -> Self {
Self::UpstreamGraph(e)
}
}
#[cfg(feature = "hdl-cat-gates")]
impl From<hdl_cat::Error> for CsaError {
fn from(e: hdl_cat::Error) -> Self {
Self::HdlCat(e)
}
}
impl core::fmt::Display for CsaError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::TreeSizeZero => write!(f, "tree size must be positive"),
Self::BitWidthMismatch { expected, actual } => write!(
f,
"bit-width mismatch: expected {expected}, got {actual}"
),
Self::ShapeMismatch { left, right } => {
write!(f, "shape mismatch: {left:?} vs {right:?}")
}
Self::InvalidGrouping { count } => {
write!(f, "invalid CSA grouping size: {count} (expected 3)")
}
Self::UpstreamGraph(e) => write!(f, "graph error: {e}"),
#[cfg(feature = "hdl-cat-gates")]
Self::HdlCat(e) => write!(f, "hdl-cat error: {e}"),
}
}
}
impl std::error::Error for CsaError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::TreeSizeZero
| Self::BitWidthMismatch { .. }
| Self::ShapeMismatch { .. }
| Self::InvalidGrouping { .. } => None,
Self::UpstreamGraph(e) => Some(e),
#[cfg(feature = "hdl-cat-gates")]
Self::HdlCat(e) => Some(e),
}
}
}
#[cfg(test)]
mod tests {
use super::{CsaError, Shape};
use comp_cat_rs::collapse::free_category::{Edge, FreeCategoryError};
use std::error::Error;
#[test]
fn display_covers_all_variants() {
let shape = Shape::new(3, 8);
let cases: Vec<CsaError> = vec![
CsaError::TreeSizeZero,
CsaError::BitWidthMismatch { expected: 8, actual: 16 },
CsaError::ShapeMismatch { left: shape, right: shape },
CsaError::InvalidGrouping { count: 4 },
CsaError::UpstreamGraph(FreeCategoryError::EdgeOutOfBounds {
edge: Edge::new(0),
count: 0,
}),
];
cases.iter().for_each(|e| {
let rendered = format!("{e}");
assert!(!rendered.is_empty());
});
}
#[test]
fn source_only_for_upstream() {
let edge = Edge::new(0);
let upstream = CsaError::UpstreamGraph(FreeCategoryError::EdgeOutOfBounds {
edge,
count: 0,
});
assert!(upstream.source().is_some());
assert!(CsaError::TreeSizeZero.source().is_none());
}
#[test]
fn from_free_category_error_works() {
let e: CsaError = FreeCategoryError::CompositionMismatch {
target: comp_cat_rs::collapse::free_category::Vertex::new(0),
source: comp_cat_rs::collapse::free_category::Vertex::new(1),
}
.into();
match e {
CsaError::UpstreamGraph(_) => {}
CsaError::TreeSizeZero
| CsaError::BitWidthMismatch { .. }
| CsaError::ShapeMismatch { .. }
| CsaError::InvalidGrouping { .. } => panic!("wrong variant"),
#[cfg(feature = "hdl-cat-gates")]
CsaError::HdlCat(_) => panic!("wrong variant"),
}
}
}