use std::fmt;
use crate::SparseError;
macro_rules! typed_index {
($(#[$doc:meta])* $name:ident, $what:literal, $prefix:literal) => {
$(#[$doc])*
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
pub struct $name(pub u32);
impl $name {
#[doc = concat!("Builds a ", $what, " index from a `usize`.")]
#[inline]
pub fn from_usize(i: usize) -> Self {
Self(u32::try_from(i).expect(concat!($what, " index exceeds u32::MAX")))
}
#[inline]
pub const fn index(self) -> usize {
self.0 as usize
}
}
impl From<u32> for $name {
#[inline]
fn from(i: u32) -> Self {
Self(i)
}
}
impl From<$name> for usize {
#[inline]
fn from(i: $name) -> usize {
i.index()
}
}
impl fmt::Display for $name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, concat!($prefix, "{}"), self.0)
}
}
};
}
typed_index!(
Col,
"column",
"c"
);
typed_index!(
Row,
"row",
"r"
);
pub(crate) fn dim_to_u32(what: &'static str, value: usize) -> Result<u32, SparseError> {
u32::try_from(value).map_err(|_| SparseError::TooLarge { what, value })
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn round_trip_and_display() {
let j = Col::from_usize(7);
assert_eq!(j.index(), 7);
assert_eq!(usize::from(j), 7);
assert_eq!(j.to_string(), "c7");
assert_eq!(Row::from(3).to_string(), "r3");
}
#[test]
#[cfg(target_pointer_width = "64")]
#[should_panic(expected = "exceeds u32::MAX")]
fn from_usize_rejects_overflow() {
let _ = Row::from_usize(1 << 33);
}
#[test]
fn dim_to_u32_reports_overflow() {
assert_eq!(dim_to_u32("rows", 5), Ok(5));
#[cfg(target_pointer_width = "64")]
assert!(matches!(
dim_to_u32("rows", 1 << 33),
Err(SparseError::TooLarge { .. })
));
}
}