kmp-domain 0.1.6

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

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TemporalDirection {
    Goto,
    Near,
    Rewind,
    Forward,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TemporalCursor {
    Ref(String),
    Time(String),
    Sequence(u32),
}

impl TemporalCursor {
    pub fn ref_id(value: impl Into<String>) -> Result<Self, DomainError> {
        let value = normalize_required(value.into(), "temporal cursor ref")?;
        Ok(Self::Ref(value))
    }

    pub fn time(value: impl Into<String>) -> Result<Self, DomainError> {
        let value = normalize_required(value.into(), "temporal cursor time")?;
        Ok(Self::Time(value))
    }

    pub fn sequence(value: u32) -> Result<Self, DomainError> {
        if value == 0 {
            return Err(DomainError::InvalidState(
                "temporal cursor sequence must be greater than zero".to_string(),
            ));
        }
        Ok(Self::Sequence(value))
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TemporalWindow {
    before_entries: usize,
    after_entries: usize,
}

impl TemporalWindow {
    pub fn new(before_entries: usize, after_entries: usize) -> Self {
        Self {
            before_entries,
            after_entries,
        }
    }

    pub fn before_entries(&self) -> usize {
        self.before_entries
    }

    pub fn after_entries(&self) -> usize {
        self.after_entries
    }
}

impl Default for TemporalWindow {
    fn default() -> Self {
        Self {
            before_entries: 2,
            after_entries: 2,
        }
    }
}

fn normalize_required(value: String, field: &'static str) -> Result<String, DomainError> {
    let value = value.trim();
    if value.is_empty() {
        Err(DomainError::EmptyValue(field))
    } else {
        Ok(value.to_string())
    }
}

#[cfg(test)]
mod tests {
    use crate::{DomainError, TemporalCursor};

    #[test]
    fn cursor_requires_non_empty_values() {
        assert_eq!(
            TemporalCursor::ref_id(" ").expect_err("empty ref must fail"),
            DomainError::EmptyValue("temporal cursor ref")
        );
        assert!(TemporalCursor::sequence(1).is_ok());
        assert!(TemporalCursor::sequence(0).is_err());
    }
}