use std::vec::Vec;
use arrow_array::{PrimitiveArray, types::ArrowPrimitiveType};
use oxgraph_layout_util::SnapshotWidth;
use oxgraph_topology::{DenseElementIndex, DenseIncidenceIndex, DenseRelationIndex, TopologyBase};
use crate::model::{IdFamily, PropertyError};
pub const SNAPSHOT_KIND_PROPERTY_DESCRIPTORS_BASE: u32 = 0x0100;
pub const SNAPSHOT_KIND_PROPERTY_DATA_BASE: u32 = 0x0104;
pub const SNAPSHOT_KIND_IDENTITY_MODES_BASE: u32 = 0x0110;
pub const SNAPSHOT_KIND_ELEMENT_IDENTITY_MAP_BASE: u32 = 0x0114;
pub const SNAPSHOT_KIND_RELATION_IDENTITY_MAP_BASE: u32 = 0x0118;
pub const SNAPSHOT_KIND_INCIDENCE_IDENTITY_MAP_BASE: u32 = 0x011C;
pub const SNAPSHOT_PROPERTY_VERSION: u32 = 1;
mod sealed {
pub trait PropertyIndex {}
pub trait PropertySnapshotMetaWord {}
pub trait PropertyAxis {}
}
pub trait PropertyIndex: sealed::PropertyIndex + SnapshotWidth + Into<u64> {
type ArrowType: ArrowPrimitiveType<Native = Self> + 'static;
fn primitive_array(values: Vec<Self>) -> PrimitiveArray<Self::ArrowType>;
}
pub trait PropertySnapshotMetaWord: sealed::PropertySnapshotMetaWord + PropertyIndex {
const PROPERTY_DESCRIPTORS_KIND: u32 =
SNAPSHOT_KIND_PROPERTY_DESCRIPTORS_BASE | Self::WIDTH_CODE;
const PROPERTY_DATA_KIND: u32 = SNAPSHOT_KIND_PROPERTY_DATA_BASE | Self::WIDTH_CODE;
const IDENTITY_MODES_KIND: u32 = SNAPSHOT_KIND_IDENTITY_MODES_BASE | Self::WIDTH_CODE;
const ELEMENT_IDENTITY_MAP_KIND: u32 =
SNAPSHOT_KIND_ELEMENT_IDENTITY_MAP_BASE | Self::WIDTH_CODE;
const RELATION_IDENTITY_MAP_KIND: u32 =
SNAPSHOT_KIND_RELATION_IDENTITY_MAP_BASE | Self::WIDTH_CODE;
const INCIDENCE_IDENTITY_MAP_KIND: u32 =
SNAPSHOT_KIND_INCIDENCE_IDENTITY_MAP_BASE | Self::WIDTH_CODE;
}
macro_rules! impl_property_width {
($index:ty, $arrow:ty) => {
impl sealed::PropertyIndex for $index {}
impl PropertyIndex for $index {
type ArrowType = $arrow;
fn primitive_array(values: Vec<Self>) -> PrimitiveArray<Self::ArrowType> {
PrimitiveArray::<$arrow>::from(values)
}
}
impl sealed::PropertySnapshotMetaWord for $index {}
impl PropertySnapshotMetaWord for $index {}
};
}
impl_property_width!(u16, arrow_array::types::UInt16Type);
impl_property_width!(u32, arrow_array::types::UInt32Type);
impl_property_width!(u64, arrow_array::types::UInt64Type);
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: DenseElementIndex,
{
fn axis_bound(&self) -> usize {
self.element_bound()
}
}
impl<T> AxisIndex<RelationAxis> for T
where
T: DenseRelationIndex,
{
fn axis_bound(&self) -> usize {
self.relation_bound()
}
}
impl<T> AxisIndex<IncidenceAxis> for T
where
T: DenseIncidenceIndex,
{
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).into()
}
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",
})
}