data_modelling_sdk/export/
dataflow.rs

1//! Data Flow format exporter for lightweight Data Flow nodes and relationships.
2//!
3//! This module exports Data Flow format YAML files (lightweight format separate from ODCS).
4//! ODCS format is only for Data Models (tables), while this format is for Data Flow nodes and relationships.
5
6use crate::models::{DataModel, Relationship, Table};
7use serde_yaml;
8
9/// Data Flow format exporter
10pub struct DataFlowExporter;
11
12impl Default for DataFlowExporter {
13    fn default() -> Self {
14        Self::new()
15    }
16}
17
18impl DataFlowExporter {
19    /// Create a new Data Flow exporter instance.
20    ///
21    /// # Example
22    ///
23    /// ```rust
24    /// use data_modelling_sdk::export::dataflow::DataFlowExporter;
25    ///
26    /// let exporter = DataFlowExporter::new();
27    /// ```
28    pub fn new() -> Self {
29        Self
30    }
31
32    /// Export a DataModel to Data Flow format YAML.
33    ///
34    /// # Arguments
35    ///
36    /// * `model` - The DataModel to export
37    ///
38    /// # Returns
39    ///
40    /// A YAML string in Data Flow format.
41    ///
42    /// # Example
43    ///
44    /// ```rust
45    /// use data_modelling_sdk::export::dataflow::DataFlowExporter;
46    /// use data_modelling_sdk::models::{DataModel, Table, Column};
47    ///
48    /// let mut model = DataModel::new("test".to_string(), "/tmp".to_string(), "control.yaml".to_string());
49    /// let table = Table::new("users".to_string(), vec![Column::new("id".to_string(), "INT".to_string())]);
50    /// model.tables.push(table);
51    ///
52    /// let yaml = DataFlowExporter::export_model(&model);
53    /// assert!(yaml.contains("nodes:"));
54    /// ```
55    pub fn export_model(model: &DataModel) -> String {
56        let mut yaml = serde_yaml::Mapping::new();
57
58        // Export nodes
59        let nodes: Vec<serde_yaml::Value> =
60            model.tables.iter().map(Self::export_node_to_yaml).collect();
61        if !nodes.is_empty() {
62            yaml.insert(
63                serde_yaml::Value::String("nodes".to_string()),
64                serde_yaml::Value::Sequence(nodes),
65            );
66        }
67
68        // Export relationships
69        let relationships: Vec<serde_yaml::Value> = model
70            .relationships
71            .iter()
72            .map(Self::export_relationship_to_yaml)
73            .collect();
74        if !relationships.is_empty() {
75            yaml.insert(
76                serde_yaml::Value::String("relationships".to_string()),
77                serde_yaml::Value::Sequence(relationships),
78            );
79        }
80
81        serde_yaml::to_string(&yaml).unwrap_or_default()
82    }
83
84    /// Export a single node (Table) to Data Flow format YAML.
85    ///
86    /// # Arguments
87    ///
88    /// * `table` - The table to export
89    ///
90    /// # Returns
91    ///
92    /// A YAML string for a single node.
93    pub fn export_node(table: &Table) -> String {
94        let yaml_value = Self::export_node_to_yaml(table);
95        serde_yaml::to_string(&yaml_value).unwrap_or_default()
96    }
97
98    /// Export a single relationship to Data Flow format YAML.
99    ///
100    /// # Arguments
101    ///
102    /// * `relationship` - The relationship to export
103    ///
104    /// # Returns
105    ///
106    /// A YAML string for a single relationship.
107    pub fn export_relationship(relationship: &Relationship) -> String {
108        let yaml_value = Self::export_relationship_to_yaml(relationship);
109        serde_yaml::to_string(&yaml_value).unwrap_or_default()
110    }
111
112    fn export_node_to_yaml(table: &Table) -> serde_yaml::Value {
113        let mut node = serde_yaml::Mapping::new();
114
115        node.insert(
116            serde_yaml::Value::String("id".to_string()),
117            serde_yaml::Value::String(table.id.to_string()),
118        );
119        node.insert(
120            serde_yaml::Value::String("name".to_string()),
121            serde_yaml::Value::String(table.name.clone()),
122        );
123        node.insert(
124            serde_yaml::Value::String("type".to_string()),
125            serde_yaml::Value::String("table".to_string()),
126        );
127
128        // Export columns
129        let columns: Vec<serde_yaml::Value> = table
130            .columns
131            .iter()
132            .map(|col| {
133                let mut col_map = serde_yaml::Mapping::new();
134                col_map.insert(
135                    serde_yaml::Value::String("name".to_string()),
136                    serde_yaml::Value::String(col.name.clone()),
137                );
138                col_map.insert(
139                    serde_yaml::Value::String("type".to_string()),
140                    serde_yaml::Value::String(col.data_type.clone()),
141                );
142                serde_yaml::Value::Mapping(col_map)
143            })
144            .collect();
145        if !columns.is_empty() {
146            node.insert(
147                serde_yaml::Value::String("columns".to_string()),
148                serde_yaml::Value::Sequence(columns),
149            );
150        }
151
152        // Export metadata
153        let metadata = Self::export_metadata_to_yaml(
154            table.owner.as_deref(),
155            table.sla.as_ref(),
156            table.contact_details.as_ref(),
157            table.infrastructure_type,
158            table.notes.as_deref(),
159        );
160        if let Some(meta) = metadata {
161            node.insert(serde_yaml::Value::String("metadata".to_string()), meta);
162        }
163
164        serde_yaml::Value::Mapping(node)
165    }
166
167    fn export_relationship_to_yaml(relationship: &Relationship) -> serde_yaml::Value {
168        let mut rel = serde_yaml::Mapping::new();
169
170        rel.insert(
171            serde_yaml::Value::String("id".to_string()),
172            serde_yaml::Value::String(relationship.id.to_string()),
173        );
174        rel.insert(
175            serde_yaml::Value::String("source_node_id".to_string()),
176            serde_yaml::Value::String(relationship.source_table_id.to_string()),
177        );
178        rel.insert(
179            serde_yaml::Value::String("target_node_id".to_string()),
180            serde_yaml::Value::String(relationship.target_table_id.to_string()),
181        );
182
183        // Export metadata
184        let metadata = Self::export_metadata_to_yaml(
185            relationship.owner.as_deref(),
186            relationship.sla.as_ref(),
187            relationship.contact_details.as_ref(),
188            relationship.infrastructure_type,
189            relationship.notes.as_deref(),
190        );
191        if let Some(meta) = metadata {
192            rel.insert(serde_yaml::Value::String("metadata".to_string()), meta);
193        }
194
195        serde_yaml::Value::Mapping(rel)
196    }
197
198    fn export_metadata_to_yaml(
199        owner: Option<&str>,
200        sla: Option<&Vec<crate::models::SlaProperty>>,
201        contact_details: Option<&crate::models::ContactDetails>,
202        infrastructure_type: Option<crate::models::enums::InfrastructureType>,
203        notes: Option<&str>,
204    ) -> Option<serde_yaml::Value> {
205        let mut has_metadata = false;
206        let mut metadata = serde_yaml::Mapping::new();
207
208        if let Some(owner_str) = owner {
209            metadata.insert(
210                serde_yaml::Value::String("owner".to_string()),
211                serde_yaml::Value::String(owner_str.to_string()),
212            );
213            has_metadata = true;
214        }
215
216        if let Some(sla_vec) = sla {
217            let sla_yaml: Vec<serde_yaml::Value> = sla_vec
218                .iter()
219                .map(|sla_prop| {
220                    let mut sla_map = serde_yaml::Mapping::new();
221                    sla_map.insert(
222                        serde_yaml::Value::String("property".to_string()),
223                        serde_yaml::Value::String(sla_prop.property.clone()),
224                    );
225                    sla_map.insert(
226                        serde_yaml::Value::String("value".to_string()),
227                        Self::json_value_to_yaml(&sla_prop.value),
228                    );
229                    sla_map.insert(
230                        serde_yaml::Value::String("unit".to_string()),
231                        serde_yaml::Value::String(sla_prop.unit.clone()),
232                    );
233                    if let Some(ref element) = sla_prop.element {
234                        sla_map.insert(
235                            serde_yaml::Value::String("element".to_string()),
236                            serde_yaml::Value::String(element.clone()),
237                        );
238                    }
239                    if let Some(ref driver) = sla_prop.driver {
240                        sla_map.insert(
241                            serde_yaml::Value::String("driver".to_string()),
242                            serde_yaml::Value::String(driver.clone()),
243                        );
244                    }
245                    if let Some(ref description) = sla_prop.description {
246                        sla_map.insert(
247                            serde_yaml::Value::String("description".to_string()),
248                            serde_yaml::Value::String(description.clone()),
249                        );
250                    }
251                    if let Some(ref scheduler) = sla_prop.scheduler {
252                        sla_map.insert(
253                            serde_yaml::Value::String("scheduler".to_string()),
254                            serde_yaml::Value::String(scheduler.clone()),
255                        );
256                    }
257                    if let Some(ref schedule) = sla_prop.schedule {
258                        sla_map.insert(
259                            serde_yaml::Value::String("schedule".to_string()),
260                            serde_yaml::Value::String(schedule.clone()),
261                        );
262                    }
263                    serde_yaml::Value::Mapping(sla_map)
264                })
265                .collect();
266            metadata.insert(
267                serde_yaml::Value::String("sla".to_string()),
268                serde_yaml::Value::Sequence(sla_yaml),
269            );
270            has_metadata = true;
271        }
272
273        if let Some(contact) = contact_details {
274            let mut contact_map = serde_yaml::Mapping::new();
275            if let Some(ref email) = contact.email {
276                contact_map.insert(
277                    serde_yaml::Value::String("email".to_string()),
278                    serde_yaml::Value::String(email.clone()),
279                );
280            }
281            if let Some(ref phone) = contact.phone {
282                contact_map.insert(
283                    serde_yaml::Value::String("phone".to_string()),
284                    serde_yaml::Value::String(phone.clone()),
285                );
286            }
287            if let Some(ref name) = contact.name {
288                contact_map.insert(
289                    serde_yaml::Value::String("name".to_string()),
290                    serde_yaml::Value::String(name.clone()),
291                );
292            }
293            if let Some(ref role) = contact.role {
294                contact_map.insert(
295                    serde_yaml::Value::String("role".to_string()),
296                    serde_yaml::Value::String(role.clone()),
297                );
298            }
299            if let Some(ref other) = contact.other {
300                contact_map.insert(
301                    serde_yaml::Value::String("other".to_string()),
302                    serde_yaml::Value::String(other.clone()),
303                );
304            }
305            if !contact_map.is_empty() {
306                metadata.insert(
307                    serde_yaml::Value::String("contact_details".to_string()),
308                    serde_yaml::Value::Mapping(contact_map),
309                );
310                has_metadata = true;
311            }
312        }
313
314        if let Some(infra_type) = infrastructure_type {
315            // Serialize enum as PascalCase string
316            let infra_str = serde_json::to_string(&infra_type)
317                .unwrap_or_default()
318                .trim_matches('"')
319                .to_string();
320            metadata.insert(
321                serde_yaml::Value::String("infrastructure_type".to_string()),
322                serde_yaml::Value::String(infra_str),
323            );
324            has_metadata = true;
325        }
326
327        if let Some(notes_str) = notes {
328            metadata.insert(
329                serde_yaml::Value::String("notes".to_string()),
330                serde_yaml::Value::String(notes_str.to_string()),
331            );
332            has_metadata = true;
333        }
334
335        if has_metadata {
336            Some(serde_yaml::Value::Mapping(metadata))
337        } else {
338            None
339        }
340    }
341
342    fn json_value_to_yaml(json: &serde_json::Value) -> serde_yaml::Value {
343        match json {
344            serde_json::Value::Null => serde_yaml::Value::Null,
345            serde_json::Value::Bool(b) => serde_yaml::Value::Bool(*b),
346            serde_json::Value::Number(n) => {
347                if let Some(i) = n.as_i64() {
348                    serde_yaml::Value::Number(serde_yaml::Number::from(i))
349                } else if let Some(f) = n.as_f64() {
350                    serde_yaml::Value::Number(serde_yaml::Number::from(f))
351                } else {
352                    serde_yaml::Value::String(n.to_string())
353                }
354            }
355            serde_json::Value::String(s) => serde_yaml::Value::String(s.clone()),
356            serde_json::Value::Array(arr) => {
357                let yaml_arr: Vec<serde_yaml::Value> =
358                    arr.iter().map(Self::json_value_to_yaml).collect();
359                serde_yaml::Value::Sequence(yaml_arr)
360            }
361            serde_json::Value::Object(obj) => {
362                let mut yaml_map = serde_yaml::Mapping::new();
363                for (k, v) in obj {
364                    yaml_map.insert(
365                        serde_yaml::Value::String(k.clone()),
366                        Self::json_value_to_yaml(v),
367                    );
368                }
369                serde_yaml::Value::Mapping(yaml_map)
370            }
371        }
372    }
373}