weavatrix_memory/domain/
node.rs1use 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 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}