data_modelling_core/models/
domain_config.rs1use chrono::{DateTime, Utc};
11use serde::{Deserialize, Serialize};
12use std::collections::HashMap;
13use uuid::Uuid;
14
15#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
17pub struct DomainOwner {
18 #[serde(skip_serializing_if = "Option::is_none")]
20 pub name: Option<String>,
21 #[serde(skip_serializing_if = "Option::is_none")]
23 pub email: Option<String>,
24 #[serde(skip_serializing_if = "Option::is_none")]
26 pub team: Option<String>,
27 #[serde(skip_serializing_if = "Option::is_none")]
29 pub role: Option<String>,
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
34pub struct ViewPosition {
35 pub x: f64,
37 pub y: f64,
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
49pub struct DomainConfig {
50 pub id: Uuid,
52 pub workspace_id: Uuid,
54 pub name: String,
56 #[serde(skip_serializing_if = "Option::is_none")]
58 pub description: Option<String>,
59 pub created_at: DateTime<Utc>,
61 pub last_modified_at: DateTime<Utc>,
63 #[serde(skip_serializing_if = "Option::is_none")]
65 pub owner: Option<DomainOwner>,
66 #[serde(default, skip_serializing_if = "Vec::is_empty")]
68 pub systems: Vec<Uuid>,
69 #[serde(default, skip_serializing_if = "Vec::is_empty")]
71 pub tables: Vec<Uuid>,
72 #[serde(default, skip_serializing_if = "Vec::is_empty")]
74 pub products: Vec<Uuid>,
75 #[serde(default, skip_serializing_if = "Vec::is_empty")]
77 pub assets: Vec<Uuid>,
78 #[serde(default, skip_serializing_if = "Vec::is_empty")]
80 pub processes: Vec<Uuid>,
81 #[serde(default, skip_serializing_if = "Vec::is_empty")]
83 pub decisions: Vec<Uuid>,
84 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
88 pub view_positions: HashMap<String, HashMap<String, ViewPosition>>,
89 #[serde(skip_serializing_if = "Option::is_none")]
91 pub folder_path: Option<String>,
92 #[serde(skip_serializing_if = "Option::is_none")]
94 pub workspace_path: Option<String>,
95}
96
97impl DomainConfig {
98 pub fn new(name: String, workspace_id: Uuid) -> Self {
100 let now = Utc::now();
101 Self {
102 id: Uuid::new_v4(),
103 workspace_id,
104 name,
105 description: None,
106 created_at: now,
107 last_modified_at: now,
108 owner: None,
109 systems: Vec::new(),
110 tables: Vec::new(),
111 products: Vec::new(),
112 assets: Vec::new(),
113 processes: Vec::new(),
114 decisions: Vec::new(),
115 view_positions: HashMap::new(),
116 folder_path: None,
117 workspace_path: None,
118 }
119 }
120
121 pub fn with_id(id: Uuid, name: String, workspace_id: Uuid) -> Self {
123 let now = Utc::now();
124 Self {
125 id,
126 workspace_id,
127 name,
128 description: None,
129 created_at: now,
130 last_modified_at: now,
131 owner: None,
132 systems: Vec::new(),
133 tables: Vec::new(),
134 products: Vec::new(),
135 assets: Vec::new(),
136 processes: Vec::new(),
137 decisions: Vec::new(),
138 view_positions: HashMap::new(),
139 folder_path: None,
140 workspace_path: None,
141 }
142 }
143
144 pub fn with_description(mut self, description: String) -> Self {
146 self.description = Some(description);
147 self
148 }
149
150 pub fn with_owner(mut self, owner: DomainOwner) -> Self {
152 self.owner = Some(owner);
153 self
154 }
155
156 pub fn add_table(&mut self, table_id: Uuid) {
158 if !self.tables.contains(&table_id) {
159 self.tables.push(table_id);
160 self.last_modified_at = Utc::now();
161 }
162 }
163
164 pub fn remove_table(&mut self, table_id: Uuid) -> bool {
166 let initial_len = self.tables.len();
167 self.tables.retain(|&id| id != table_id);
168 if self.tables.len() != initial_len {
169 self.last_modified_at = Utc::now();
170 true
171 } else {
172 false
173 }
174 }
175
176 pub fn add_product(&mut self, product_id: Uuid) {
178 if !self.products.contains(&product_id) {
179 self.products.push(product_id);
180 self.last_modified_at = Utc::now();
181 }
182 }
183
184 pub fn add_asset(&mut self, asset_id: Uuid) {
186 if !self.assets.contains(&asset_id) {
187 self.assets.push(asset_id);
188 self.last_modified_at = Utc::now();
189 }
190 }
191
192 pub fn add_process(&mut self, process_id: Uuid) {
194 if !self.processes.contains(&process_id) {
195 self.processes.push(process_id);
196 self.last_modified_at = Utc::now();
197 }
198 }
199
200 pub fn add_decision(&mut self, decision_id: Uuid) {
202 if !self.decisions.contains(&decision_id) {
203 self.decisions.push(decision_id);
204 self.last_modified_at = Utc::now();
205 }
206 }
207
208 pub fn add_system(&mut self, system_id: Uuid) {
210 if !self.systems.contains(&system_id) {
211 self.systems.push(system_id);
212 self.last_modified_at = Utc::now();
213 }
214 }
215
216 pub fn set_view_position(&mut self, view_mode: &str, entity_id: &str, x: f64, y: f64) {
218 let positions = self
219 .view_positions
220 .entry(view_mode.to_string())
221 .or_default();
222 positions.insert(entity_id.to_string(), ViewPosition { x, y });
223 self.last_modified_at = Utc::now();
224 }
225
226 pub fn get_view_position(&self, view_mode: &str, entity_id: &str) -> Option<&ViewPosition> {
228 self.view_positions
229 .get(view_mode)
230 .and_then(|positions| positions.get(entity_id))
231 }
232
233 pub fn from_yaml(yaml_content: &str) -> Result<Self, serde_yaml::Error> {
235 serde_yaml::from_str(yaml_content)
236 }
237
238 pub fn to_yaml(&self) -> Result<String, serde_yaml::Error> {
240 serde_yaml::to_string(self)
241 }
242
243 pub fn from_json(json_content: &str) -> Result<Self, serde_json::Error> {
245 serde_json::from_str(json_content)
246 }
247
248 pub fn to_json(&self) -> Result<String, serde_json::Error> {
250 serde_json::to_string(self)
251 }
252
253 pub fn to_json_pretty(&self) -> Result<String, serde_json::Error> {
255 serde_json::to_string_pretty(self)
256 }
257}
258
259#[cfg(test)]
260mod tests {
261 use super::*;
262
263 #[test]
264 fn test_domain_config_new() {
265 let workspace_id = Uuid::new_v4();
266 let config = DomainConfig::new("Customer Management".to_string(), workspace_id);
267
268 assert_eq!(config.name, "Customer Management");
269 assert_eq!(config.workspace_id, workspace_id);
270 assert!(config.tables.is_empty());
271 assert!(config.products.is_empty());
272 }
273
274 #[test]
275 fn test_domain_config_add_table() {
276 let mut config = DomainConfig::new("Test".to_string(), Uuid::new_v4());
277 let table_id = Uuid::new_v4();
278
279 config.add_table(table_id);
280 assert_eq!(config.tables.len(), 1);
281 assert_eq!(config.tables[0], table_id);
282
283 config.add_table(table_id);
285 assert_eq!(config.tables.len(), 1);
286 }
287
288 #[test]
289 fn test_domain_config_view_positions() {
290 let mut config = DomainConfig::new("Test".to_string(), Uuid::new_v4());
291 let entity_id = Uuid::new_v4().to_string();
292
293 config.set_view_position("systems", &entity_id, 100.0, 200.0);
294
295 let pos = config.get_view_position("systems", &entity_id);
296 assert!(pos.is_some());
297 let pos = pos.unwrap();
298 assert_eq!(pos.x, 100.0);
299 assert_eq!(pos.y, 200.0);
300 }
301
302 #[test]
303 fn test_domain_config_yaml_roundtrip() {
304 let workspace_id = Uuid::new_v4();
305 let mut config = DomainConfig::new("Finance".to_string(), workspace_id);
306 config.description = Some("Financial data domain".to_string());
307 config.owner = Some(DomainOwner {
308 name: Some("Jane Doe".to_string()),
309 email: Some("jane@example.com".to_string()),
310 team: Some("Data Team".to_string()),
311 role: Some("Data Owner".to_string()),
312 });
313 config.add_table(Uuid::new_v4());
314 config.add_product(Uuid::new_v4());
315
316 let yaml = config.to_yaml().unwrap();
317 let parsed = DomainConfig::from_yaml(&yaml).unwrap();
318
319 assert_eq!(config.id, parsed.id);
320 assert_eq!(config.name, parsed.name);
321 assert_eq!(config.description, parsed.description);
322 assert_eq!(config.tables.len(), parsed.tables.len());
323 }
324
325 #[test]
326 fn test_domain_config_json_roundtrip() {
327 let config = DomainConfig::new("Test".to_string(), Uuid::new_v4());
328
329 let json = config.to_json().unwrap();
330 let parsed = DomainConfig::from_json(&json).unwrap();
331
332 assert_eq!(config.id, parsed.id);
333 assert_eq!(config.name, parsed.name);
334 }
335}