pub trait TrySparseIndex: Sized {
type Error: std::error::Error;
fn index(&self) -> usize;
fn from_index(index: usize) -> Self {
Self::try_from_index(index).unwrap()
}
fn try_from_index(index: usize) -> Result<Self, Self::Error>;
fn validate_sorted(indices: impl DoubleEndedIterator<Item = usize>) -> Result<(), Self::Error> {
for index in indices {
Self::try_from_index(index)?;
}
Ok(())
}
}
fn validate_extremes<T: TrySparseIndex>(
mut indices: impl DoubleEndedIterator<Item = usize>,
) -> Result<(), T::Error> {
if let Some(max) = indices.next_back() {
T::try_from_index(max)?;
}
if let Some(min) = indices.next() {
T::try_from_index(min)?;
}
Ok(())
}
impl<T: SparseIndex> TrySparseIndex for T {
type Error = std::convert::Infallible;
fn index(&self) -> usize {
self.index()
}
fn from_index(index: usize) -> Self {
Self::from_index(index)
}
fn try_from_index(index: usize) -> Result<Self, Self::Error> {
Ok(Self::from_index(index))
}
fn validate_sorted(_: impl DoubleEndedIterator<Item = usize>) -> Result<(), Self::Error> {
Ok(())
}
}
pub trait SparseIndex: Sized {
fn index(&self) -> usize;
fn from_index(index: usize) -> Self;
}
#[derive(Debug, thiserror::Error)]
pub enum NumericIndexError {
#[error("index out of bounds: {received} is not in [0..{max}]")]
OutOfBounds {
max: usize,
received: usize,
},
}
macro_rules! impl_integers {
($($u:ty => $i:ty),*) => {
$(
impl TrySparseIndex for $u {
type Error = NumericIndexError;
#[inline]
fn index(&self) -> usize {
*self as usize
}
#[inline]
fn from_index(index: usize) -> Self {
index as $u
}
#[inline]
fn try_from_index(index: usize) -> Result<Self, Self::Error> {
if index > <$u>::MAX as usize {
Err(NumericIndexError::OutOfBounds {
max: <$u>::MAX as usize,
received: index,
})
} else {
Ok(Self::from_index(index))
}
}
#[inline]
fn validate_sorted(
indices: impl DoubleEndedIterator<Item = usize>,
) -> Result<(), Self::Error> {
validate_extremes::<Self>(indices)
}
}
impl TrySparseIndex for $i {
type Error = NumericIndexError;
#[inline]
fn index(&self) -> usize {
*self as usize
}
#[inline]
fn from_index(index: usize) -> Self {
index as $i
}
#[inline]
fn try_from_index(index: usize) -> Result<Self, Self::Error> {
if index > <$i>::MAX as usize {
Err(NumericIndexError::OutOfBounds {
max: <$i>::MAX as usize,
received: index,
})
} else {
Ok(Self::from_index(index))
}
}
#[inline]
fn validate_sorted(
indices: impl DoubleEndedIterator<Item = usize>,
) -> Result<(), Self::Error> {
validate_extremes::<Self>(indices)
}
}
impl TrySparseIndex for std::num::NonZero<$i> {
type Error = NumericIndexError;
#[inline]
fn index(&self) -> usize {
self.get() as usize - 1
}
#[inline]
fn from_index(index: usize) -> Self {
let value = index
.checked_add(1)
.and_then(|v| <$i>::try_from(v).ok())
.expect("index out of range");
Self::new(value).expect("index + 1 is non-zero")
}
#[inline]
fn try_from_index(index: usize) -> Result<Self, Self::Error> {
if index >= <$i>::MAX as usize {
Err(NumericIndexError::OutOfBounds {
max: <$i>::MAX as usize - 1,
received: index,
})
} else {
Ok(Self::from_index(index))
}
}
#[inline]
fn validate_sorted(
indices: impl DoubleEndedIterator<Item = usize>,
) -> Result<(), Self::Error> {
validate_extremes::<Self>(indices)
}
}
impl TrySparseIndex for std::num::NonZero<$u> {
type Error = NumericIndexError;
#[inline]
fn index(&self) -> usize {
self.get() as usize - 1
}
#[inline]
fn from_index(index: usize) -> Self {
let value = index
.checked_add(1)
.and_then(|v| <$u>::try_from(v).ok())
.expect("index out of range");
Self::new(value).expect("index + 1 is non-zero")
}
#[inline]
fn try_from_index(index: usize) -> Result<Self, Self::Error> {
if index >= <$u>::MAX as usize {
Err(NumericIndexError::OutOfBounds {
max: <$u>::MAX as usize - 1,
received: index,
})
} else {
Ok(Self::from_index(index))
}
}
#[inline]
fn validate_sorted(
indices: impl DoubleEndedIterator<Item = usize>,
) -> Result<(), Self::Error> {
validate_extremes::<Self>(indices)
}
}
)*
};
}
impl_integers!(
u8 => i8,
u16 => i16,
u32 => i32,
u64 => i64,
usize => isize
);