kmp-domain 0.1.9

Domain model of the Kernel Memory Protocol: aggregates, value objects, repositories and projections, with no IO
Documentation
use std::cmp::Ordering;

use crate::TemporalCoordinate;

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub(super) enum TemporalAxis {
    Time,
    Sequence,
    Rank,
    Ref,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(super) struct TemporalAxisKey {
    axis: TemporalAxis,
    value: String,
}

impl TemporalAxisKey {
    pub(super) fn time(value: &str) -> Self {
        Self {
            axis: TemporalAxis::Time,
            value: value.to_string(),
        }
    }

    pub(super) fn sequence(value: u32) -> Self {
        Self {
            axis: TemporalAxis::Sequence,
            value: format!("{value:010}"),
        }
    }

    fn rank(value: u32) -> Self {
        Self {
            axis: TemporalAxis::Rank,
            value: format!("{value:010}"),
        }
    }

    fn ref_id(value: &str) -> Self {
        Self {
            axis: TemporalAxis::Ref,
            value: value.to_string(),
        }
    }

    pub(super) fn axis(&self) -> TemporalAxis {
        self.axis
    }

    pub(super) fn from_coordinate(ref_id: &str, coordinate: &TemporalCoordinate) -> Vec<Self> {
        let mut keys = Vec::new();
        if let Some(value) = coordinate
            .occurred_at()
            .or(coordinate.valid_from())
            .or(coordinate.observed_at())
            .or(coordinate.ingested_at())
        {
            keys.push(Self::time(value));
        }
        if let Some(value) = coordinate.sequence() {
            keys.push(Self::sequence(value));
        }
        if let Some(value) = coordinate.rank() {
            keys.push(Self::rank(value));
        }
        if keys.is_empty() {
            keys.push(Self::ref_id(ref_id));
        }

        keys
    }
}

impl PartialOrd for TemporalAxisKey {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for TemporalAxisKey {
    fn cmp(&self, other: &Self) -> Ordering {
        self.axis
            .cmp(&other.axis)
            .then_with(|| self.value.cmp(&other.value))
    }
}

pub(super) fn primary_coordinate_key(coordinate: &TemporalCoordinate) -> TemporalAxisKey {
    TemporalAxisKey::from_coordinate("", coordinate)
        .into_iter()
        .next()
        .expect("coordinate key should always exist")
}