use crate::error::{Error, Result};
use num::{CheckedAdd, FromPrimitive, Unsigned};
use std::fmt::{Debug, Display};
use std::hash::Hash;
use std::num::TryFromIntError;
use std::ops::AddAssign;
pub trait VertexRef:
Unsigned
+ TryInto<usize>
+ TryFrom<usize, Error = TryFromIntError>
+ TryFrom<u32>
+ FromPrimitive
+ CheckedAdd
+ Copy
+ Debug
+ Default
+ Display
+ PartialEq
+ Eq
+ PartialOrd
+ Ord
+ Hash
{
const MAX: Self;
const MIN: Self;
}
impl VertexRef for u16 {
const MAX: Self = u16::MAX;
const MIN: Self = u16::MIN;
}
impl VertexRef for u32 {
const MAX: Self = u32::MAX;
const MIN: Self = u32::MIN;
}
impl VertexRef for u64 {
const MAX: Self = u64::MAX;
const MIN: Self = u64::MIN;
}
pub type VertexIndex16 = VertexIndex<u16>;
pub type VertexIndex32 = VertexIndex<u32>;
pub type VertexIndex64 = VertexIndex<u64>;
#[derive(Copy, Clone, Default, Debug, Eq, Ord, PartialOrd, PartialEq, Hash)]
#[repr(transparent)]
pub struct VertexIndex<T: VertexRef>(T);
impl<T: VertexRef> VertexIndex<T> {
#[inline]
pub fn new(value: T) -> Self {
Self(value)
}
#[inline]
pub fn value(&self) -> T {
self.0
}
#[inline]
pub fn try_to_usize(&self) -> Result<usize> {
self.0.try_into().map_err(|_| Error::IndexConversion {
source_type: std::any::type_name::<T>().to_string(),
target_type: "usize".to_string(),
value: self.0.to_string(),
})
}
#[inline]
pub fn to_usize(&self) -> usize {
self.try_to_usize().unwrap_or_else(|_| {
panic!(
"vertex index {} does not fit in usize on this target; use try_to_usize()",
self.0
)
})
}
#[inline]
#[must_use]
pub fn from_u32(value: u32) -> Option<Self> {
T::from_u32(value).map(|v| Self::new(v))
}
#[inline]
pub fn is_max(&self) -> bool {
self.0 == T::MAX
}
#[inline]
pub fn is_zero(&self) -> bool {
self.0 == T::MIN
}
#[inline]
pub fn next(&self) -> Option<Self> {
self.0.checked_add(&T::from_u8(1)?).map(Self::new)
}
#[inline]
pub fn checked_add(self, other: Self) -> Option<Self> {
self.0.checked_add(&other.0).map(Self::new)
}
#[inline]
pub fn try_add_assign(&mut self, other: Self) -> Result<()> {
let sum = self
.checked_add(other)
.ok_or_else(|| Error::IndexOverflow {
index_type: std::any::type_name::<T>().to_string(),
value: self.value().to_string(),
})?;
*self = sum;
Ok(())
}
}
impl<T: VertexRef> Display for VertexIndex<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl<T: VertexRef> AddAssign for VertexIndex<T> {
fn add_assign(&mut self, other: Self) {
*self = self
.checked_add(other)
.unwrap_or_else(|| VertexIndex::new(T::MAX));
}
}
impl TryFrom<VertexIndex<u16>> for VertexIndex<u32> {
type Error = Error;
fn try_from(value: VertexIndex<u16>) -> Result<Self> {
Ok(VertexIndex(u32::from(value.0)))
}
}
impl TryFrom<VertexIndex<u16>> for VertexIndex<u64> {
type Error = Error;
fn try_from(value: VertexIndex<u16>) -> Result<Self> {
Ok(VertexIndex(u64::from(value.0)))
}
}
impl TryFrom<VertexIndex<u32>> for VertexIndex<u64> {
type Error = Error;
fn try_from(value: VertexIndex<u32>) -> Result<Self> {
Ok(VertexIndex(u64::from(value.0)))
}
}
impl TryFrom<VertexIndex<u32>> for VertexIndex<u16> {
type Error = Error;
fn try_from(value: VertexIndex<u32>) -> Result<Self> {
u16::try_from(value.0)
.map(VertexIndex)
.map_err(|_| Error::IndexConversion {
source_type: "u32".to_string(),
target_type: "u16".to_string(),
value: value.0.to_string(),
})
}
}
impl TryFrom<VertexIndex<u64>> for VertexIndex<u32> {
type Error = Error;
fn try_from(value: VertexIndex<u64>) -> Result<Self> {
u32::try_from(value.0)
.map(VertexIndex)
.map_err(|_| Error::IndexConversion {
source_type: "u64".to_string(),
target_type: "u32".to_string(),
value: value.0.to_string(),
})
}
}
impl TryFrom<VertexIndex<u64>> for VertexIndex<u16> {
type Error = Error;
fn try_from(value: VertexIndex<u64>) -> Result<Self> {
u16::try_from(value.0)
.map(VertexIndex)
.map_err(|_| Error::IndexConversion {
source_type: "u64".to_string(),
target_type: "u16".to_string(),
value: value.0.to_string(),
})
}
}
impl From<u16> for VertexIndex<u16> {
fn from(value: u16) -> Self {
Self(value)
}
}
impl From<u16> for VertexIndex<u32> {
fn from(value: u16) -> Self {
Self(u32::from(value))
}
}
impl From<u16> for VertexIndex<u64> {
fn from(value: u16) -> Self {
Self(u64::from(value))
}
}
impl From<u32> for VertexIndex<u32> {
fn from(value: u32) -> Self {
Self(value)
}
}
impl From<u32> for VertexIndex<u64> {
fn from(value: u32) -> Self {
Self(u64::from(value))
}
}
impl From<u64> for VertexIndex<u64> {
fn from(value: u64) -> Self {
Self(value)
}
}
impl TryFrom<u32> for VertexIndex<u16> {
type Error = Error;
fn try_from(value: u32) -> Result<Self> {
u16::try_from(value)
.map(Self)
.map_err(|_| Error::IndexConversion {
source_type: "u32".to_string(),
target_type: "u16".to_string(),
value: value.to_string(),
})
}
}
impl TryFrom<u64> for VertexIndex<u16> {
type Error = Error;
fn try_from(value: u64) -> Result<Self> {
u16::try_from(value)
.map(Self)
.map_err(|_| Error::IndexConversion {
source_type: "u64".to_string(),
target_type: "u16".to_string(),
value: value.to_string(),
})
}
}
impl TryFrom<u64> for VertexIndex<u32> {
type Error = Error;
fn try_from(value: u64) -> Result<Self> {
u32::try_from(value)
.map(Self)
.map_err(|_| Error::IndexConversion {
source_type: "u64".to_string(),
target_type: "u32".to_string(),
value: value.to_string(),
})
}
}
impl<T: VertexRef> TryFrom<usize> for VertexIndex<T> {
type Error = Error;
fn try_from(value: usize) -> Result<Self> {
T::try_from(value)
.map(Self)
.map_err(|_| Error::IndexConversion {
source_type: "usize".to_string(),
target_type: std::any::type_name::<T>().to_string(),
value: value.to_string(),
})
}
}
pub trait VertexIndicesSequence<T>
where
T: VertexRef,
{
fn sequence(start: T, count: usize) -> Result<Vec<VertexIndex<T>>>;
}
impl<T: VertexRef> VertexIndicesSequence<T> for VertexIndex<T> {
fn sequence(start: T, count: usize) -> Result<Vec<VertexIndex<T>>> {
let mut result = Vec::with_capacity(count);
let mut current = start;
for _ in 0..count {
result.push(VertexIndex::new(current));
if let Some(next) = current.checked_add(&T::from_u8(1).unwrap_or_default()) {
current = next;
} else {
return Err(Error::IndexConversion {
source_type: format!("{current} + 1"),
target_type: std::any::type_name::<T>().to_string(),
value: "overflow".to_string(),
});
}
}
Ok(result)
}
}
pub struct RawVertexView<'a, VR: VertexRef>(pub(crate) &'a [VertexIndex<VR>]);
impl<VR: VertexRef> std::ops::Deref for RawVertexView<'_, VR> {
type Target = [VR];
fn deref(&self) -> &Self::Target {
const {
assert!(std::mem::size_of::<VertexIndex<VR>>() == std::mem::size_of::<VR>());
assert!(std::mem::align_of::<VertexIndex<VR>>() == std::mem::align_of::<VR>());
}
unsafe { std::slice::from_raw_parts(self.0.as_ptr().cast::<VR>(), self.0.len()) }
}
}
pub trait VertexIndexVec<T>
where
T: VertexRef,
{
fn to_vertex_indices(self) -> Vec<VertexIndex<T>>;
}
impl<T> VertexIndexVec<T> for Vec<T>
where
T: VertexRef,
{
fn to_vertex_indices(self) -> Vec<VertexIndex<T>> {
self.into_iter().map(VertexIndex::new).collect()
}
}
#[cfg(test)]
mod tests {
use super::VertexIndex32;
#[cfg(target_pointer_width = "32")]
use super::VertexIndex64;
#[test]
fn u32_index_converts_to_usize() {
let index = VertexIndex32::new(42);
assert_eq!(index.try_to_usize().unwrap(), 42);
assert_eq!(index.to_usize(), 42);
}
#[cfg(target_pointer_width = "32")]
#[test]
fn u64_index_overflow_is_reported_on_32_bit_targets() {
let index = VertexIndex64::new(u64::from(u32::MAX) + 1);
let error = index.try_to_usize().unwrap_err();
assert_eq!(
error.to_string(),
format!(
"failed to convert index from {} to usize: value {}",
std::any::type_name::<u64>(),
u64::from(u32::MAX) + 1
)
);
}
}