use std::vec::Vec;
use arrow_array::{PrimitiveArray, types::ArrowPrimitiveType};
use oxgraph_topology::{ElementIndex, IncidenceIndex, RelationIndex, TopologyBase};
use zerocopy::{
FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned,
byteorder::{LE, U16, U32, U64},
};
use crate::model::{IdFamily, PropertyError};
pub const SNAPSHOT_KIND_PROPERTY_DESCRIPTORS_U16: u32 = 0x0100;
pub const SNAPSHOT_KIND_PROPERTY_DATA_U16: u32 = 0x0101;
pub const SNAPSHOT_KIND_PROPERTY_DESCRIPTORS_U32: u32 = 0x0102;
pub const SNAPSHOT_KIND_PROPERTY_DATA_U32: u32 = 0x0103;
pub const SNAPSHOT_KIND_PROPERTY_DESCRIPTORS_U64: u32 = 0x0104;
pub const SNAPSHOT_KIND_PROPERTY_DATA_U64: u32 = 0x0105;
pub const SNAPSHOT_KIND_IDENTITY_MODES_U16: u32 = 0x0110;
pub const SNAPSHOT_KIND_IDENTITY_MODES_U32: u32 = 0x0111;
pub const SNAPSHOT_KIND_IDENTITY_MODES_U64: u32 = 0x0112;
pub const SNAPSHOT_KIND_ELEMENT_IDENTITY_MAP_U16: u32 = 0x0113;
pub const SNAPSHOT_KIND_ELEMENT_IDENTITY_MAP_U32: u32 = 0x0114;
pub const SNAPSHOT_KIND_ELEMENT_IDENTITY_MAP_U64: u32 = 0x0115;
pub const SNAPSHOT_KIND_RELATION_IDENTITY_MAP_U16: u32 = 0x0116;
pub const SNAPSHOT_KIND_RELATION_IDENTITY_MAP_U32: u32 = 0x0117;
pub const SNAPSHOT_KIND_RELATION_IDENTITY_MAP_U64: u32 = 0x0118;
pub const SNAPSHOT_KIND_INCIDENCE_IDENTITY_MAP_U16: u32 = 0x0119;
pub const SNAPSHOT_KIND_INCIDENCE_IDENTITY_MAP_U32: u32 = 0x011A;
pub const SNAPSHOT_KIND_INCIDENCE_IDENTITY_MAP_U64: u32 = 0x011B;
pub const SNAPSHOT_PROPERTY_VERSION: u32 = 1;
mod sealed {
pub trait PropertyIndex {}
pub trait PropertySnapshotMetaWord {}
pub trait PropertyAxis {}
}
pub trait PropertyIndex: sealed::PropertyIndex + Copy + Ord {
type ArrowType: ArrowPrimitiveType<Native = Self> + 'static;
type LittleEndianWord: FromBytes + Immutable + IntoBytes + KnownLayout + Unaligned + Copy;
fn to_usize(self) -> Option<usize>;
fn from_usize(value: usize) -> Option<Self>;
fn from_u64(value: u64) -> Option<Self>;
fn to_u64(self) -> u64;
fn to_le_word(self) -> Self::LittleEndianWord;
fn from_le_word(word: Self::LittleEndianWord) -> Self;
fn primitive_array(values: Vec<Self>) -> PrimitiveArray<Self::ArrowType>;
}
pub trait PropertySnapshotMetaWord: sealed::PropertySnapshotMetaWord + PropertyIndex {
const PROPERTY_DESCRIPTORS_KIND: u32;
const PROPERTY_DATA_KIND: u32;
const IDENTITY_MODES_KIND: u32;
const ELEMENT_IDENTITY_MAP_KIND: u32;
const RELATION_IDENTITY_MAP_KIND: u32;
const INCIDENCE_IDENTITY_MAP_KIND: u32;
}
macro_rules! impl_property_width {
(
$index:ty,
$arrow:ty,
$word:ty,
$descriptor_kind:expr,
$data_kind:expr,
$identity_kind:expr,
$element_kind:expr,
$relation_kind:expr,
$incidence_kind:expr
) => {
impl sealed::PropertyIndex for $index {}
impl PropertyIndex for $index {
type ArrowType = $arrow;
type LittleEndianWord = $word;
fn to_usize(self) -> Option<usize> {
usize::try_from(self).ok()
}
fn from_usize(value: usize) -> Option<Self> {
<$index>::try_from(value).ok()
}
fn from_u64(value: u64) -> Option<Self> {
<$index>::try_from(value).ok()
}
fn to_u64(self) -> u64 {
u64::from(self)
}
fn to_le_word(self) -> Self::LittleEndianWord {
<$word>::new(self)
}
fn from_le_word(word: Self::LittleEndianWord) -> Self {
word.get()
}
fn primitive_array(values: Vec<Self>) -> PrimitiveArray<Self::ArrowType> {
PrimitiveArray::<$arrow>::from(values)
}
}
impl sealed::PropertySnapshotMetaWord for $index {}
impl PropertySnapshotMetaWord for $index {
const PROPERTY_DESCRIPTORS_KIND: u32 = $descriptor_kind;
const PROPERTY_DATA_KIND: u32 = $data_kind;
const IDENTITY_MODES_KIND: u32 = $identity_kind;
const ELEMENT_IDENTITY_MAP_KIND: u32 = $element_kind;
const RELATION_IDENTITY_MAP_KIND: u32 = $relation_kind;
const INCIDENCE_IDENTITY_MAP_KIND: u32 = $incidence_kind;
}
};
}
impl_property_width!(
u16,
arrow_array::types::UInt16Type,
U16<LE>,
SNAPSHOT_KIND_PROPERTY_DESCRIPTORS_U16,
SNAPSHOT_KIND_PROPERTY_DATA_U16,
SNAPSHOT_KIND_IDENTITY_MODES_U16,
SNAPSHOT_KIND_ELEMENT_IDENTITY_MAP_U16,
SNAPSHOT_KIND_RELATION_IDENTITY_MAP_U16,
SNAPSHOT_KIND_INCIDENCE_IDENTITY_MAP_U16
);
impl_property_width!(
u32,
arrow_array::types::UInt32Type,
U32<LE>,
SNAPSHOT_KIND_PROPERTY_DESCRIPTORS_U32,
SNAPSHOT_KIND_PROPERTY_DATA_U32,
SNAPSHOT_KIND_IDENTITY_MODES_U32,
SNAPSHOT_KIND_ELEMENT_IDENTITY_MAP_U32,
SNAPSHOT_KIND_RELATION_IDENTITY_MAP_U32,
SNAPSHOT_KIND_INCIDENCE_IDENTITY_MAP_U32
);
impl_property_width!(
u64,
arrow_array::types::UInt64Type,
U64<LE>,
SNAPSHOT_KIND_PROPERTY_DESCRIPTORS_U64,
SNAPSHOT_KIND_PROPERTY_DATA_U64,
SNAPSHOT_KIND_IDENTITY_MODES_U64,
SNAPSHOT_KIND_ELEMENT_IDENTITY_MAP_U64,
SNAPSHOT_KIND_RELATION_IDENTITY_MAP_U64,
SNAPSHOT_KIND_INCIDENCE_IDENTITY_MAP_U64
);
pub trait PropertyAxis: sealed::PropertyAxis {
fn id_family() -> IdFamily;
}
#[derive(Clone, Copy, Debug, Default)]
pub struct ElementAxis;
impl sealed::PropertyAxis for ElementAxis {}
impl PropertyAxis for ElementAxis {
fn id_family() -> IdFamily {
IdFamily::Element
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct RelationAxis;
impl sealed::PropertyAxis for RelationAxis {}
impl PropertyAxis for RelationAxis {
fn id_family() -> IdFamily {
IdFamily::Relation
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct IncidenceAxis;
impl sealed::PropertyAxis for IncidenceAxis {}
impl PropertyAxis for IncidenceAxis {
fn id_family() -> IdFamily {
IdFamily::Incidence
}
}
pub trait AxisIndex<A: PropertyAxis>: TopologyBase {
fn axis_bound(&self) -> usize;
}
impl<T> AxisIndex<ElementAxis> for T
where
T: ElementIndex,
{
fn axis_bound(&self) -> usize {
self.element_bound()
}
}
impl<T> AxisIndex<RelationAxis> for T
where
T: RelationIndex,
{
fn axis_bound(&self) -> usize {
self.relation_bound()
}
}
impl<T> AxisIndex<IncidenceAxis> for T
where
T: IncidenceIndex,
{
fn axis_bound(&self) -> usize {
self.incidence_bound()
}
}
pub(crate) fn le_word<W>(value: usize) -> Result<W::LittleEndianWord, PropertyError>
where
W: PropertySnapshotMetaWord,
{
let Some(value) = W::from_usize(value) else {
return Err(PropertyError::SnapshotDescriptorMismatch {
reason: "value does not fit selected metadata width",
});
};
Ok(value.to_le_word())
}
pub(crate) fn le_word_to_usize<W>(word: W::LittleEndianWord) -> Result<usize, PropertyError>
where
W: PropertySnapshotMetaWord,
{
W::from_le_word(word)
.to_usize()
.ok_or(PropertyError::SnapshotDescriptorMismatch {
reason: "metadata word does not fit usize",
})
}
pub(crate) fn le_word_to_u64<W>(word: W::LittleEndianWord) -> u64
where
W: PropertySnapshotMetaWord,
{
W::from_le_word(word).to_u64()
}
pub(crate) fn le_word_to_u32<W>(word: W::LittleEndianWord) -> Result<u32, PropertyError>
where
W: PropertySnapshotMetaWord,
{
let value = le_word_to_u64::<W>(word);
u32::try_from(value).map_err(|_error| PropertyError::SnapshotDescriptorMismatch {
reason: "metadata word does not fit u32 tag",
})
}