use core::fmt;
use oxgraph_snapshot::SectionViewError;
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum BcsrError {
OffsetLengthOverflow {
count: usize,
},
OffsetLength {
section: BcsrSection,
expected: usize,
actual: usize,
},
HyperedgeOffsetLengthMismatch {
head_offsets_len: usize,
tail_offsets_len: usize,
},
VertexOffsetLengthMismatch {
outgoing_offsets_len: usize,
incoming_offsets_len: usize,
},
FirstOffset {
section: BcsrSection,
actual: usize,
},
NonMonotonicOffset {
section: BcsrSection,
index: usize,
previous: usize,
actual: usize,
},
FinalOffset {
section: BcsrSection,
final_offset: usize,
value_len: usize,
},
VertexOutOfRange {
section: BcsrSection,
index: usize,
vertex: usize,
vertex_count: usize,
},
HyperedgeOutOfRange {
section: BcsrSection,
index: usize,
hyperedge: usize,
hyperedge_count: usize,
},
OutgoingTotalMismatch {
head_participants_len: usize,
outgoing_hyperedges_len: usize,
},
IncomingTotalMismatch {
tail_participants_len: usize,
incoming_hyperedges_len: usize,
},
NotStrictlyAscending {
section: BcsrSection,
index: usize,
previous: usize,
actual: usize,
},
UsizeOverflow {
value: usize,
},
TotalIncidenceCountOverflow {
p_head: usize,
p_tail: usize,
},
CrossDirectionMismatch {
side: BcsrRoleSide,
hyperedge: usize,
vertex: usize,
},
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum BcsrSection {
HeadOffsets,
HeadParticipants,
TailOffsets,
TailParticipants,
VertexOutgoingOffsets,
VertexOutgoingHyperedges,
VertexIncomingOffsets,
VertexIncomingHyperedges,
}
impl fmt::Display for BcsrSection {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(match self {
Self::HeadOffsets => "BCSR_HEAD_OFFSETS",
Self::HeadParticipants => "BCSR_HEAD_PARTICIPANTS",
Self::TailOffsets => "BCSR_TAIL_OFFSETS",
Self::TailParticipants => "BCSR_TAIL_PARTICIPANTS",
Self::VertexOutgoingOffsets => "BCSR_VERTEX_OUTGOING_OFFSETS",
Self::VertexOutgoingHyperedges => "BCSR_VERTEX_OUTGOING_HYPEREDGES",
Self::VertexIncomingOffsets => "BCSR_VERTEX_INCOMING_OFFSETS",
Self::VertexIncomingHyperedges => "BCSR_VERTEX_INCOMING_HYPEREDGES",
})
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum BcsrRoleSide {
Outgoing,
Incoming,
}
impl fmt::Display for BcsrRoleSide {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(match self {
Self::Outgoing => "outgoing",
Self::Incoming => "incoming",
})
}
}
impl fmt::Display for BcsrError {
#[expect(
clippy::too_many_lines,
reason = "one flat exhaustive Display match over every BcsrError variant; splitting it reintroduces the silent _ => Ok(()) wildcards this consolidation removed"
)]
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::OffsetLengthOverflow { count } => {
write!(formatter, "offset length overflow for count {count}")
}
Self::OffsetLength {
section,
expected,
actual,
} => write!(
formatter,
"{section} has wrong length: expected {expected}, got {actual}"
),
Self::HyperedgeOffsetLengthMismatch {
head_offsets_len,
tail_offsets_len,
} => write!(
formatter,
"head_offsets length {head_offsets_len} disagrees with tail_offsets length {tail_offsets_len}"
),
Self::VertexOffsetLengthMismatch {
outgoing_offsets_len,
incoming_offsets_len,
} => write!(
formatter,
"vertex_outgoing_offsets length {outgoing_offsets_len} disagrees with vertex_incoming_offsets length {incoming_offsets_len}"
),
Self::FirstOffset { section, actual } => {
write!(formatter, "{section} first offset must be 0, got {actual}")
}
Self::NonMonotonicOffset {
section,
index,
previous,
actual,
} => write!(
formatter,
"{section} offset at index {index} is not monotonic: previous {previous}, got {actual}"
),
Self::FinalOffset {
section,
final_offset,
value_len,
} => write!(
formatter,
"{section} final offset {final_offset} does not match value length {value_len}"
),
Self::VertexOutOfRange {
section,
index,
vertex,
vertex_count,
} => write!(
formatter,
"{section} vertex {vertex} at index {index} is out of range (vertex count {vertex_count})"
),
Self::HyperedgeOutOfRange {
section,
index,
hyperedge,
hyperedge_count,
} => write!(
formatter,
"{section} hyperedge {hyperedge} at index {index} is out of range (hyperedge count {hyperedge_count})"
),
Self::NotStrictlyAscending {
section,
index,
previous,
actual,
} => write!(
formatter,
"{section} value at index {index} is not strictly ascending: previous {previous}, got {actual}"
),
Self::OutgoingTotalMismatch {
head_participants_len,
outgoing_hyperedges_len,
} => write!(
formatter,
"head_participants length {head_participants_len} disagrees with vertex_outgoing_hyperedges length {outgoing_hyperedges_len}"
),
Self::IncomingTotalMismatch {
tail_participants_len,
incoming_hyperedges_len,
} => write!(
formatter,
"tail_participants length {tail_participants_len} disagrees with vertex_incoming_hyperedges length {incoming_hyperedges_len}"
),
Self::UsizeOverflow { value } => {
write!(formatter, "BCSR index value {value} does not fit usize")
}
Self::TotalIncidenceCountOverflow { p_head, p_tail } => write!(
formatter,
"incidence ID space P_head ({p_head}) + P_tail ({p_tail}) overflows usize"
),
Self::CrossDirectionMismatch {
side,
hyperedge,
vertex,
} => write!(
formatter,
"cross-direction mismatch on {side}: hyperedge {hyperedge} and vertex {vertex} disagree"
),
}
}
}
impl core::error::Error for BcsrError {}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum BcsrSnapshotError {
MissingSection {
section: BcsrSection,
kind: u32,
},
VersionMismatch {
section: BcsrSection,
kind: u32,
expected: u32,
actual: u32,
},
SectionView {
section: BcsrSection,
error: SectionViewError,
},
OffsetsEmpty {
section: BcsrSection,
},
CountOverflow {
section: BcsrSection,
offsets_len: usize,
},
Bcsr(BcsrError),
}
impl fmt::Display for BcsrSnapshotError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::MissingSection { section, kind } => write!(
formatter,
"snapshot has no {section} section (kind 0x{kind:04x})"
),
Self::VersionMismatch {
section,
kind,
expected,
actual,
} => write!(
formatter,
"{section} section (kind 0x{kind:04x}) version {actual} does not match expected {expected}"
),
Self::SectionView { section, error } => write!(
formatter,
"{section} cannot be borrowed as little-endian u32: {error}"
),
Self::OffsetsEmpty { section } => write!(formatter, "{section} section is empty"),
Self::CountOverflow {
section,
offsets_len,
} => write!(
formatter,
"derived count from {section} length {offsets_len} does not fit u32"
),
Self::Bcsr(error) => {
write!(formatter, "bipartite-CSR validation failed: {error}")
}
}
}
}
impl core::error::Error for BcsrSnapshotError {}
impl From<BcsrError> for BcsrSnapshotError {
fn from(error: BcsrError) -> Self {
Self::Bcsr(error)
}
}