Skip to main content

kmp_domain/value_objects/
memory_dimension_identity.rs

1use crate::DomainError;
2
3const PREFIX: &str = "about:";
4const SEPARATOR: &str = ":dimension:";
5
6#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
7pub struct MemoryDimensionIdentity {
8    about: String,
9    dimension_id: String,
10}
11
12impl MemoryDimensionIdentity {
13    pub fn new(
14        about: impl Into<String>,
15        dimension_id: impl Into<String>,
16    ) -> Result<Self, DomainError> {
17        let about = normalize_required(about.into(), "memory dimension about")?;
18        let dimension_id = normalize_required(dimension_id.into(), "memory dimension id")?;
19        Ok(Self {
20            about,
21            dimension_id,
22        })
23    }
24
25    pub fn parse(value: &str) -> Option<Self> {
26        let value = value.trim().strip_prefix(PREFIX)?;
27        let (about, dimension_id) = value.split_once(SEPARATOR)?;
28        Self::new(about, dimension_id).ok()
29    }
30
31    pub fn node_id(&self) -> String {
32        format!("{PREFIX}{}{SEPARATOR}{}", self.about, self.dimension_id)
33    }
34
35    pub fn about(&self) -> &str {
36        &self.about
37    }
38
39    pub fn dimension_id(&self) -> &str {
40        &self.dimension_id
41    }
42}
43
44fn normalize_required(value: String, field: &'static str) -> Result<String, DomainError> {
45    let value = value.trim();
46    if value.is_empty() {
47        Err(DomainError::EmptyValue(field))
48    } else {
49        Ok(value.to_string())
50    }
51}
52
53#[cfg(test)]
54mod tests {
55    use super::MemoryDimensionIdentity;
56
57    #[test]
58    fn identity_formats_and_parses_namespaced_dimension_node_id() {
59        let identity =
60            MemoryDimensionIdentity::new("question:830ce83f", "timeline").expect("valid identity");
61
62        assert_eq!(
63            identity.node_id(),
64            "about:question:830ce83f:dimension:timeline"
65        );
66        let parsed = MemoryDimensionIdentity::parse(&identity.node_id()).expect("should parse");
67        assert_eq!(parsed.about(), "question:830ce83f");
68        assert_eq!(parsed.dimension_id(), "timeline");
69    }
70
71    #[test]
72    fn identity_rejects_empty_parts() {
73        assert!(MemoryDimensionIdentity::new("", "timeline").is_err());
74        assert!(MemoryDimensionIdentity::new("question:830ce83f", " ").is_err());
75    }
76}