velesdb_core/collection/graph/
schema.rs1use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9
10use crate::error::{Error, Result};
11
12#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
14#[serde(rename_all = "lowercase")]
15pub enum ValueType {
16 String,
18 Integer,
20 Float,
22 Boolean,
24 Vector,
26}
27
28#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
30pub struct NodeType {
31 name: String,
32 properties: HashMap<String, ValueType>,
33}
34
35impl NodeType {
36 #[must_use]
38 pub fn new(name: &str) -> Self {
39 Self {
40 name: name.to_string(),
41 properties: HashMap::new(),
42 }
43 }
44
45 #[must_use]
47 pub fn with_properties(mut self, properties: HashMap<String, ValueType>) -> Self {
48 self.properties = properties;
49 self
50 }
51
52 #[must_use]
54 pub fn name(&self) -> &str {
55 &self.name
56 }
57
58 #[must_use]
60 pub fn properties(&self) -> &HashMap<String, ValueType> {
61 &self.properties
62 }
63
64 #[must_use]
66 pub fn property_type(&self, name: &str) -> Option<&ValueType> {
67 self.properties.get(name)
68 }
69}
70
71#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
73pub struct EdgeType {
74 name: String,
75 from_type: String,
76 to_type: String,
77 properties: HashMap<String, ValueType>,
78}
79
80impl EdgeType {
81 #[must_use]
83 pub fn new(name: &str, from_type: &str, to_type: &str) -> Self {
84 Self {
85 name: name.to_string(),
86 from_type: from_type.to_string(),
87 to_type: to_type.to_string(),
88 properties: HashMap::new(),
89 }
90 }
91
92 #[must_use]
94 pub fn with_properties(mut self, properties: HashMap<String, ValueType>) -> Self {
95 self.properties = properties;
96 self
97 }
98
99 #[must_use]
101 pub fn name(&self) -> &str {
102 &self.name
103 }
104
105 #[must_use]
107 pub fn from_type(&self) -> &str {
108 &self.from_type
109 }
110
111 #[must_use]
113 pub fn to_type(&self) -> &str {
114 &self.to_type
115 }
116
117 #[must_use]
119 pub fn properties(&self) -> &HashMap<String, ValueType> {
120 &self.properties
121 }
122}
123
124#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
130pub struct GraphSchema {
131 schemaless: bool,
132 node_types: Vec<NodeType>,
133 edge_types: Vec<EdgeType>,
134}
135
136impl Default for GraphSchema {
137 fn default() -> Self {
138 Self::schemaless()
139 }
140}
141
142impl GraphSchema {
143 #[must_use]
147 pub fn new() -> Self {
148 Self {
149 schemaless: false,
150 node_types: Vec::new(),
151 edge_types: Vec::new(),
152 }
153 }
154
155 #[must_use]
157 pub fn schemaless() -> Self {
158 Self {
159 schemaless: true,
160 node_types: Vec::new(),
161 edge_types: Vec::new(),
162 }
163 }
164
165 #[must_use]
167 pub fn with_node_type(mut self, node_type: NodeType) -> Self {
168 self.node_types.push(node_type);
169 self
170 }
171
172 #[must_use]
174 pub fn with_edge_type(mut self, edge_type: EdgeType) -> Self {
175 self.edge_types.push(edge_type);
176 self
177 }
178
179 #[must_use]
181 pub fn is_schemaless(&self) -> bool {
182 self.schemaless
183 }
184
185 #[must_use]
187 pub fn node_types(&self) -> &[NodeType] {
188 &self.node_types
189 }
190
191 #[must_use]
193 pub fn edge_types(&self) -> &[EdgeType] {
194 &self.edge_types
195 }
196
197 #[must_use]
199 pub fn has_node_type(&self, name: &str) -> bool {
200 self.node_types.iter().any(|nt| nt.name == name)
201 }
202
203 #[must_use]
205 pub fn has_edge_type(&self, name: &str) -> bool {
206 self.edge_types.iter().any(|et| et.name == name)
207 }
208
209 pub fn validate_node_type(&self, type_name: &str) -> Result<()> {
217 if self.schemaless {
218 return Ok(());
219 }
220
221 if self.has_node_type(type_name) {
222 return Ok(());
223 }
224
225 let allowed: Vec<&str> = self.node_types.iter().map(|nt| nt.name.as_str()).collect();
226 Err(Error::SchemaValidation(format!(
227 "Node type '{type_name}' not allowed. Valid types: {allowed:?}",
228 )))
229 }
230
231 pub fn validate_edge_type(
240 &self,
241 edge_type: &str,
242 from_type: &str,
243 to_type: &str,
244 ) -> Result<()> {
245 if self.schemaless {
246 return Ok(());
247 }
248
249 let edge_def = self.edge_types.iter().find(|et| et.name == edge_type);
251
252 if let Some(def) = edge_def {
253 if def.from_type != from_type {
255 return Err(Error::SchemaValidation(format!(
256 "Edge '{edge_type}' expects source type '{}', got '{from_type}'",
257 def.from_type
258 )));
259 }
260 if def.to_type != to_type {
262 return Err(Error::SchemaValidation(format!(
263 "Edge '{edge_type}' expects target type '{}', got '{to_type}'",
264 def.to_type
265 )));
266 }
267 if !self.has_node_type(from_type) {
269 return Err(Error::SchemaValidation(format!(
270 "Edge '{edge_type}' references undeclared source node type '{from_type}'",
271 )));
272 }
273 if !self.has_node_type(to_type) {
274 return Err(Error::SchemaValidation(format!(
275 "Edge '{edge_type}' references undeclared target node type '{to_type}'",
276 )));
277 }
278 Ok(())
279 } else {
280 let allowed: Vec<&str> = self.edge_types.iter().map(|et| et.name.as_str()).collect();
281 Err(Error::SchemaValidation(format!(
282 "Edge type '{edge_type}' not allowed. Valid types: {allowed:?}",
283 )))
284 }
285 }
286
287 #[must_use]
289 pub fn get_node_type(&self, name: &str) -> Option<&NodeType> {
290 self.node_types.iter().find(|nt| nt.name == name)
291 }
292
293 #[must_use]
295 pub fn get_edge_type(&self, name: &str) -> Option<&EdgeType> {
296 self.edge_types.iter().find(|et| et.name == name)
297 }
298}