Skip to main content

weavatrix_memory/domain/
node.rs

1use crate::{
2    error::{MemoryError, Result},
3    id::EntityId,
4};
5use serde::{Deserialize, Serialize};
6use std::collections::BTreeMap;
7
8#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
9pub struct MemoryNode {
10    pub id: EntityId,
11    pub kind: String,
12    pub label: String,
13    #[serde(skip_serializing_if = "Option::is_none")]
14    pub repository: Option<String>,
15    #[serde(skip_serializing_if = "Option::is_none")]
16    pub branch: Option<String>,
17    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
18    pub attributes: BTreeMap<String, String>,
19}
20
21impl MemoryNode {
22    /// Creates a memory entity.
23    ///
24    /// # Errors
25    ///
26    /// Rejects empty or whitespace-padded kind and an empty label.
27    pub fn new(id: EntityId, kind: impl Into<String>, label: impl Into<String>) -> Result<Self> {
28        let node = Self {
29            id,
30            kind: kind.into(),
31            label: label.into(),
32            repository: None,
33            branch: None,
34            attributes: BTreeMap::new(),
35        };
36        node.validate()?;
37        Ok(node)
38    }
39
40    #[must_use]
41    pub fn in_repository(mut self, repository: impl Into<String>) -> Self {
42        self.repository = Some(repository.into());
43        self
44    }
45
46    #[must_use]
47    pub fn on_branch(mut self, branch: impl Into<String>) -> Self {
48        self.branch = Some(branch.into());
49        self
50    }
51
52    #[must_use]
53    pub fn with_attribute(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
54        self.attributes.insert(key.into(), value.into());
55        self
56    }
57
58    pub(crate) fn validate(&self) -> Result<()> {
59        super::validate_text("node.kind", &self.kind)?;
60        if self.label.is_empty() {
61            return Err(MemoryError::InvalidValue {
62                field: "node.label",
63                reason: "must be non-empty",
64            });
65        }
66        super::validate_optional_text("node.repository", self.repository.as_deref())?;
67        super::validate_optional_text("node.branch", self.branch.as_deref())?;
68        for key in self.attributes.keys() {
69            super::validate_text("node.attribute.key", key)?;
70        }
71        Ok(())
72    }
73}