graphrecords-query 0.5.0

High-performance graph-based data records
Documentation
use super::{engine::Session, plan::PlanNode, stats::Stats};
use crate::Operand;
use std::marker::PhantomData;

pub(super) type ErasedRule<O> =
    Box<dyn for<'a> Fn(O, &Session<'a>) -> Transformed<O> + Send + Sync>;

pub struct Transformed<T> {
    value: T,
    changed: bool,
}

impl<T> Transformed<T> {
    #[must_use]
    pub const fn changed(value: T) -> Self {
        Self {
            value,
            changed: true,
        }
    }

    #[must_use]
    pub const fn unchanged(value: T) -> Self {
        Self {
            value,
            changed: false,
        }
    }

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

    #[must_use]
    pub const fn is_changed(&self) -> bool {
        self.changed
    }

    #[must_use]
    pub fn into_parts(self) -> (T, bool) {
        (self.value, self.changed)
    }
}

pub trait Rule<O: Operand>: 'static + Send + Sync {
    fn apply(&self, operand: O, stats: &Stats) -> Transformed<O>;
}

#[must_use]
pub fn rule<C, O, F>(rewrite: F) -> impl Rule<O>
where
    C: PlanNode,
    O: Operand + 'static,
    F: Fn(&C, &Stats) -> Option<O> + Send + Sync + 'static,
{
    ContextRule {
        rewrite,
        matched: PhantomData,
    }
}

struct ContextRule<C, O, F> {
    rewrite: F,
    matched: PhantomData<fn() -> (C, O)>,
}

impl<C, O, F> Rule<O> for ContextRule<C, O, F>
where
    C: PlanNode,
    O: Operand + 'static,
    F: Fn(&C, &Stats) -> Option<O> + Send + Sync + 'static,
{
    fn apply(&self, operand: O, stats: &Stats) -> Transformed<O> {
        let Some(context) = operand.as_plan_node().downcast::<C>() else {
            return Transformed::unchanged(operand);
        };

        match (self.rewrite)(context, stats) {
            Some(rewritten) => Transformed::changed(rewritten),
            None => Transformed::unchanged(operand),
        }
    }
}