graphrecords-query 0.5.0

High-performance graph-based data records
Documentation
use crate::{EntityDomain, IndexDomain, Unit, ValueDomain};
use std::any::{TypeId, type_name};

struct IndexValueDescriptor;
struct EntityReferenceDescriptor;

#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct DomainDescriptor {
    identifier: TypeId,
    name: &'static str,
}

impl DomainDescriptor {
    #[must_use]
    pub fn of<T: 'static>() -> Self {
        Self {
            identifier: TypeId::of::<T>(),
            name: type_name::<T>(),
        }
    }

    #[must_use]
    pub fn is<T: 'static>(&self) -> bool {
        self.identifier == TypeId::of::<T>()
    }

    #[must_use]
    pub const fn name(&self) -> &'static str {
        self.name
    }
}

#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum IndexDescriptor {
    Domain(DomainDescriptor),
    Expanded { parent: Box<Self>, child: Box<Self> },
    ExpandedSource { parent: Box<Self> },
}

impl IndexDescriptor {
    #[must_use]
    pub fn domain<I: IndexDomain>() -> Self {
        Self::Domain(DomainDescriptor::of::<I>())
    }

    #[must_use]
    pub fn expanded(parent: Self, child: Self) -> Self {
        Self::Expanded {
            parent: Box::new(parent),
            child: Box::new(child),
        }
    }

    #[must_use]
    pub fn expanded_source(parent: Self) -> Self {
        Self::ExpandedSource {
            parent: Box::new(parent),
        }
    }
}

#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum ValueRole {
    Value,
    Index(IndexDescriptor),
    EntityReference(IndexDescriptor),
    Unit,
}

#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct ValueDescriptor {
    domain: DomainDescriptor,
    role: ValueRole,
}

impl ValueDescriptor {
    #[must_use]
    pub fn value<V: ValueDomain>() -> Self {
        Self {
            domain: DomainDescriptor::of::<V>(),
            role: ValueRole::Value,
        }
    }

    #[must_use]
    pub fn index(index: IndexDescriptor) -> Self {
        Self {
            domain: DomainDescriptor::of::<IndexValueDescriptor>(),
            role: ValueRole::Index(index),
        }
    }

    #[must_use]
    pub fn entity_reference<E: EntityDomain>() -> Self {
        Self {
            domain: DomainDescriptor::of::<EntityReferenceDescriptor>(),
            role: ValueRole::EntityReference(IndexDescriptor::domain::<E>()),
        }
    }

    #[must_use]
    pub fn entity_reference_index(index: IndexDescriptor) -> Self {
        Self {
            domain: DomainDescriptor::of::<EntityReferenceDescriptor>(),
            role: ValueRole::EntityReference(index),
        }
    }

    #[must_use]
    pub fn unit() -> Self {
        Self {
            domain: DomainDescriptor::of::<Unit>(),
            role: ValueRole::Unit,
        }
    }

    #[must_use]
    pub const fn domain(&self) -> &DomainDescriptor {
        &self.domain
    }

    #[must_use]
    pub const fn role(&self) -> &ValueRole {
        &self.role
    }
}

#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum LaneShapeDescriptor {
    Indexed {
        index: IndexDescriptor,
        value: ValueDescriptor,
    },
    Bare {
        value: ValueDescriptor,
    },
}

impl LaneShapeDescriptor {
    #[must_use]
    pub const fn value(&self) -> &ValueDescriptor {
        match self {
            Self::Indexed { value, .. } | Self::Bare { value } => value,
        }
    }
}

#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum OrderDescriptor {
    Ordered,
    Unordered,
}

#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum ArityDescriptor {
    Multiple { order: OrderDescriptor },
    Single,
    Definite,
}

#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum OperandDescriptor {
    Lane {
        shape: LaneShapeDescriptor,
        arity: ArityDescriptor,
    },
    Group {
        member: IndexDescriptor,
        key: IndexDescriptor,
        payload: Box<Self>,
    },
}

impl OperandDescriptor {
    #[must_use]
    pub fn lane_shape(&self) -> &LaneShapeDescriptor {
        match self {
            Self::Lane { shape, .. } => shape,
            Self::Group { payload, .. } => payload.lane_shape(),
        }
    }

    #[must_use]
    pub fn lane_arity(&self) -> ArityDescriptor {
        match self {
            Self::Lane { arity, .. } => *arity,
            Self::Group { payload, .. } => payload.lane_arity(),
        }
    }

    #[must_use]
    pub fn group_depth(&self) -> usize {
        match self {
            Self::Lane { .. } => 0,
            Self::Group { payload, .. } => 1 + payload.group_depth(),
        }
    }

    #[must_use]
    pub fn with_lane_value(&self, value: ValueDescriptor) -> Self {
        match self {
            Self::Lane { shape, arity } => Self::Lane {
                shape: match shape {
                    LaneShapeDescriptor::Indexed { index, .. } => LaneShapeDescriptor::Indexed {
                        index: index.clone(),
                        value,
                    },
                    LaneShapeDescriptor::Bare { .. } => LaneShapeDescriptor::Bare { value },
                },
                arity: *arity,
            },
            Self::Group {
                member,
                key,
                payload,
            } => Self::Group {
                member: member.clone(),
                key: key.clone(),
                payload: Box::new(payload.with_lane_value(value)),
            },
        }
    }
}

#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default)]
pub enum RetentionDescriptor {
    #[default]
    Preserving,
    Dropping,
}

#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum ArgumentValueSource {
    Literal(ValueDescriptor),
    Operand(OperandDescriptor),
}

impl ArgumentValueSource {
    #[must_use]
    pub fn value(&self) -> &ValueDescriptor {
        match self {
            Self::Literal(value) => value,
            Self::Operand(operand) => operand.lane_shape().value(),
        }
    }
}

#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum ArgumentMissingPolicy {
    None,
    Drop,
    Replace(ArgumentValueSource),
}

#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct ValueArgumentDescriptor {
    source: ArgumentValueSource,
    missing: ArgumentMissingPolicy,
}

impl ValueArgumentDescriptor {
    #[must_use]
    pub const fn literal(value: ValueDescriptor) -> Self {
        Self {
            source: ArgumentValueSource::Literal(value),
            missing: ArgumentMissingPolicy::None,
        }
    }

    #[must_use]
    pub const fn operand(operand: OperandDescriptor) -> Self {
        Self {
            source: ArgumentValueSource::Operand(operand),
            missing: ArgumentMissingPolicy::None,
        }
    }

    #[must_use]
    pub fn with_missing(self, missing: ArgumentMissingPolicy) -> Self {
        Self {
            source: self.source,
            missing,
        }
    }

    #[must_use]
    pub const fn source(&self) -> &ArgumentValueSource {
        &self.source
    }

    #[must_use]
    pub const fn missing(&self) -> &ArgumentMissingPolicy {
        &self.missing
    }

    #[must_use]
    pub fn value(&self) -> &ValueDescriptor {
        self.source.value()
    }

    #[must_use]
    pub const fn retention(&self) -> RetentionDescriptor {
        match self.missing {
            ArgumentMissingPolicy::Drop => RetentionDescriptor::Dropping,
            ArgumentMissingPolicy::None | ArgumentMissingPolicy::Replace(_) => {
                RetentionDescriptor::Preserving
            }
        }
    }
}

#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum ArgumentDescriptor {
    Value(ValueArgumentDescriptor),
    Field(DomainDescriptor),
    Selector(DomainDescriptor),
    Operand(OperandDescriptor),
}

impl ArgumentDescriptor {
    #[must_use]
    pub fn field<T: 'static>() -> Self {
        Self::Field(DomainDescriptor::of::<T>())
    }

    #[must_use]
    pub fn selector<T: 'static>() -> Self {
        Self::Selector(DomainDescriptor::of::<T>())
    }
}