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