Skip to main content

kmp_domain/value_objects/
temporal_cursor.rs

1use crate::DomainError;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4pub enum TemporalDirection {
5    Goto,
6    Near,
7    Rewind,
8    Forward,
9}
10
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub enum TemporalCursor {
13    Ref(String),
14    Time(String),
15    Sequence(u32),
16}
17
18impl TemporalCursor {
19    pub fn ref_id(value: impl Into<String>) -> Result<Self, DomainError> {
20        let value = normalize_required(value.into(), "temporal cursor ref")?;
21        Ok(Self::Ref(value))
22    }
23
24    pub fn time(value: impl Into<String>) -> Result<Self, DomainError> {
25        let value = normalize_required(value.into(), "temporal cursor time")?;
26        Ok(Self::Time(value))
27    }
28
29    pub fn sequence(value: u32) -> Result<Self, DomainError> {
30        if value == 0 {
31            return Err(DomainError::InvalidState(
32                "temporal cursor sequence must be greater than zero".to_string(),
33            ));
34        }
35        Ok(Self::Sequence(value))
36    }
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub struct TemporalWindow {
41    before_entries: usize,
42    after_entries: usize,
43}
44
45impl TemporalWindow {
46    pub fn new(before_entries: usize, after_entries: usize) -> Self {
47        Self {
48            before_entries,
49            after_entries,
50        }
51    }
52
53    pub fn before_entries(&self) -> usize {
54        self.before_entries
55    }
56
57    pub fn after_entries(&self) -> usize {
58        self.after_entries
59    }
60}
61
62impl Default for TemporalWindow {
63    fn default() -> Self {
64        Self {
65            before_entries: 2,
66            after_entries: 2,
67        }
68    }
69}
70
71fn normalize_required(value: String, field: &'static str) -> Result<String, DomainError> {
72    let value = value.trim();
73    if value.is_empty() {
74        Err(DomainError::EmptyValue(field))
75    } else {
76        Ok(value.to_string())
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use crate::{DomainError, TemporalCursor};
83
84    #[test]
85    fn cursor_requires_non_empty_values() {
86        assert_eq!(
87            TemporalCursor::ref_id(" ").expect_err("empty ref must fail"),
88            DomainError::EmptyValue("temporal cursor ref")
89        );
90        assert!(TemporalCursor::sequence(1).is_ok());
91        assert!(TemporalCursor::sequence(0).is_err());
92    }
93}