mod compact_id_map;
pub use compact_id_map::CompactIdMap;
use std::{fmt::Debug, hash::Hash, marker::PhantomData};
use super::borrow::OwnableRef;
#[doc(alias = "IndexType")]
pub trait IdType: Clone + Ord + Hash + Debug {
fn sentinel() -> Self;
fn is_integer() -> bool;
fn as_bits(&self) -> u64;
fn from_bits(bits: u64) -> Self;
fn as_usize(&self) -> usize {
self.as_bits() as usize
}
fn from_usize(id: usize) -> Self {
Self::from_bits(id as u64)
}
fn is_sentinel(&self) -> bool {
self == &Self::sentinel()
}
}
pub trait IntegerIdType: IdType + Copy + From<usize> + Into<usize> {}
impl<T: IdType, U: IdType> IdType for (T, U) {
fn sentinel() -> Self {
(T::sentinel(), U::sentinel())
}
fn is_integer() -> bool {
false
}
fn as_bits(&self) -> u64 {
panic!("unsupported")
}
fn from_bits(_: u64) -> Self {
panic!("unsupported")
}
}
pub trait AsIdRef<I: IdType> {
fn as_id(&self) -> OwnableRef<'_, I>;
}
#[doc(alias = "NodeIndex")]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct VertexId<N = u64>(N);
#[doc(alias = "EdgeIndex")]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct EdgeId<N = u64>(N);
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Virtual<I>(u64, PhantomData<I>);
impl<I: IdType> IdType for Virtual<I> {
fn sentinel() -> Self {
Self::from_bits(u64::MAX)
}
fn is_integer() -> bool {
true
}
fn as_bits(&self) -> u64 {
self.0
}
fn from_bits(bits: u64) -> Self {
Self(bits, PhantomData)
}
}
impl<I: IdType> From<usize> for Virtual<I> {
fn from(index: usize) -> Self {
Self::from_usize(index)
}
}
impl<I: IdType> From<Virtual<I>> for usize {
fn from(id: Virtual<I>) -> Self {
id.as_usize()
}
}
impl<I: IdType> From<u64> for Virtual<I> {
fn from(bits: u64) -> Self {
Self::from_bits(bits)
}
}
impl<I: IdType> From<Virtual<I>> for u64 {
fn from(id: Virtual<I>) -> Self {
id.as_bits()
}
}
impl<I: IntegerIdType> IntegerIdType for Virtual<I> {}
pub trait IdPair {
type VertexId: IdType;
type EdgeId: IdType;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum DefaultId {}
impl IdPair for DefaultId {
type VertexId = VertexId;
type EdgeId = EdgeId;
}
pub struct CustomId<VI, EI> {
ty: PhantomData<fn() -> (VI, EI)>,
}
impl<VI: IdType, EI: IdType> IdPair for CustomId<VI, EI> {
type VertexId = VI;
type EdgeId = EI;
}
pub(crate) trait UseId<Id: IdPair> {
type Id: IdType;
}
pub(crate) enum UseVertexId {}
impl<Id: IdPair> UseId<Id> for UseVertexId {
type Id = Id::VertexId;
}
#[allow(unused)]
pub(crate) enum UseEdgeId {}
impl<Id: IdPair> UseId<Id> for UseEdgeId {
type Id = Id::EdgeId;
}
macro_rules! impl_int_id {
($id_ty:ident, $int_ty:ty) => {
impl IdType for $id_ty<$int_ty> {
fn sentinel() -> Self {
Self(<$int_ty>::MAX)
}
fn is_integer() -> bool {
true
}
fn as_bits(&self) -> u64 {
self.0 as u64
}
fn from_bits(bits: u64) -> Self {
Self(bits as $int_ty)
}
fn as_usize(&self) -> usize {
self.0.try_into().expect("id type overflow")
}
fn from_usize(index: usize) -> Self {
Self(index.try_into().expect("id type overflow"))
}
}
impl From<usize> for $id_ty<$int_ty> {
fn from(index: usize) -> Self {
Self::from_usize(index)
}
}
impl From<$id_ty<$int_ty>> for usize {
fn from(id: $id_ty<$int_ty>) -> Self {
id.as_usize()
}
}
impl IntegerIdType for $id_ty<$int_ty> {}
};
}
impl_int_id!(VertexId, usize);
impl_int_id!(VertexId, u64);
impl_int_id!(VertexId, u32);
impl_int_id!(VertexId, u16);
impl_int_id!(VertexId, u8);
impl_int_id!(EdgeId, usize);
impl_int_id!(EdgeId, u64);
impl_int_id!(EdgeId, u32);
impl_int_id!(EdgeId, u16);
impl_int_id!(EdgeId, u8);
impl IdType for () {
#[allow(clippy::unused_unit)]
fn sentinel() -> Self {
()
}
fn is_integer() -> bool {
false
}
fn as_bits(&self) -> u64 {
panic!("unsupported")
}
fn from_bits(_: u64) -> Self {
panic!("unsupported")
}
}
impl<I> AsIdRef<I> for I
where
I: IdType,
{
fn as_id(&self) -> OwnableRef<'_, I> {
OwnableRef::Borrowed(self)
}
}
impl<I> AsIdRef<I> for &I
where
I: IdType,
{
fn as_id(&self) -> OwnableRef<'_, I> {
OwnableRef::Borrowed(self)
}
}
impl<I> AsIdRef<I> for usize
where
I: IntegerIdType,
{
fn as_id(&self) -> OwnableRef<'_, I> {
OwnableRef::Owned(I::from(*self))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn supports_different_id_variants() {
fn inner(_id: &VertexId) {}
fn outer<I>(id: I)
where
I: AsIdRef<VertexId>,
{
inner(id.as_id().as_ref())
}
outer(VertexId::from_usize(3));
#[allow(clippy::needless_borrows_for_generic_args)]
outer(&VertexId::from_usize(3));
outer(3);
}
}