Skip to main content

data_modelling_core/export/
cads.rs

1//! CADS (Compute Asset Description Specification) exporter
2//!
3//! Exports CADSAsset models to CADS v1.0 YAML format.
4
5use crate::export::ExportError;
6use crate::models::cads::*;
7
8/// CADS exporter for generating CADS v1.0 YAML from CADSAsset models
9pub struct CADSExporter;
10
11impl CADSExporter {
12    /// Export a CADS asset to CADS v1.0 YAML format (instance method for WASM compatibility)
13    ///
14    /// # Arguments
15    ///
16    /// * `asset` - The CADS asset to export
17    ///
18    /// # Returns
19    ///
20    /// A Result containing the YAML string in CADS v1.0 format, or an ExportError
21    pub fn export(&self, asset: &CADSAsset) -> Result<String, ExportError> {
22        let yaml = Self::export_asset(asset);
23
24        // Validate exported YAML against CADS schema (if feature enabled)
25        #[cfg(feature = "schema-validation")]
26        {
27            use crate::validation::schema::validate_cads_internal;
28            validate_cads_internal(&yaml).map_err(ExportError::ValidationError)?;
29        }
30
31        Ok(yaml)
32    }
33
34    /// Export a CADS asset to CADS v1.0 YAML format
35    ///
36    /// # Arguments
37    ///
38    /// * `asset` - The CADS asset to export
39    ///
40    /// # Returns
41    ///
42    /// A YAML string in CADS v1.0 format
43    ///
44    /// # Example
45    ///
46    /// ```rust
47    /// use data_modelling_core::export::cads::CADSExporter;
48    /// use data_modelling_core::models::cads::*;
49    ///
50    /// let asset = CADSAsset {
51    ///     api_version: "v1.0".to_string(),
52    ///     kind: CADSKind::AIModel,
53    ///     id: "550e8400-e29b-41d4-a716-446655440000".to_string(),
54    ///     name: "sentiment-analysis-model".to_string(),
55    ///     version: "1.0.0".to_string(),
56    ///     status: CADSStatus::Production,
57    ///     domain: None,
58    ///     domain_id: None,
59    ///     tags: vec![],
60    ///     description: None,
61    ///     runtime: None,
62    ///     sla: None,
63    ///     pricing: None,
64    ///     team: None,
65    ///     risk: None,
66    ///     compliance: None,
67    ///     validation_profiles: None,
68    ///     bpmn_models: None,
69    ///     dmn_models: None,
70    ///     openapi_specs: None,
71    ///     custom_properties: None,
72    ///     created_at: None,
73    ///     updated_at: None,
74    /// };
75    ///
76    /// let yaml = CADSExporter::export_asset(&asset);
77    /// assert!(yaml.contains("apiVersion: v1.0"));
78    /// assert!(yaml.contains("kind: AIModel"));
79    /// ```
80    pub fn export_asset(asset: &CADSAsset) -> String {
81        // Use direct struct serialization - serde handles all field naming and optional fields
82        match serde_yaml::to_string(asset) {
83            Ok(yaml) => yaml,
84            Err(e) => format!("# Error serializing asset: {}\n", e),
85        }
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92
93    #[test]
94    fn test_export_asset_basic() {
95        let asset = CADSAsset {
96            api_version: "v1.0".to_string(),
97            kind: CADSKind::AIModel,
98            id: "test-id".to_string(),
99            name: "Test Model".to_string(),
100            version: "1.0.0".to_string(),
101            status: CADSStatus::Production,
102            domain: Some("ml".to_string()),
103            domain_id: None,
104            tags: vec![],
105            description: None,
106            runtime: None,
107            sla: None,
108            pricing: None,
109            team: None,
110            risk: None,
111            compliance: None,
112            validation_profiles: None,
113            bpmn_models: None,
114            dmn_models: None,
115            openapi_specs: None,
116            custom_properties: None,
117            created_at: None,
118            updated_at: None,
119        };
120
121        let yaml = CADSExporter::export_asset(&asset);
122        assert!(yaml.contains("apiVersion: v1.0"));
123        assert!(yaml.contains("kind: AIModel"));
124        assert!(yaml.contains("id: test-id"));
125        assert!(yaml.contains("status: production"));
126        assert!(yaml.contains("name: Test Model"));
127        assert!(yaml.contains("domain: ml"));
128    }
129
130    #[test]
131    fn test_export_asset_with_description() {
132        let asset = CADSAsset {
133            api_version: "v1.0".to_string(),
134            kind: CADSKind::MLPipeline,
135            id: "test-id".to_string(),
136            name: "Test Pipeline".to_string(),
137            version: "1.0.0".to_string(),
138            status: CADSStatus::Draft,
139            domain: None,
140            domain_id: None,
141            tags: vec![],
142            description: Some(CADSDescription {
143                purpose: Some("Data processing pipeline".to_string()),
144                limitations: Some("Max 1TB per day".to_string()),
145                usage: None,
146                external_links: None,
147            }),
148            runtime: None,
149            sla: None,
150            pricing: None,
151            team: None,
152            risk: None,
153            compliance: None,
154            validation_profiles: None,
155            bpmn_models: None,
156            dmn_models: None,
157            openapi_specs: None,
158            custom_properties: None,
159            created_at: None,
160            updated_at: None,
161        };
162
163        let yaml = CADSExporter::export_asset(&asset);
164        assert!(yaml.contains("description:"));
165        assert!(yaml.contains("purpose: Data processing pipeline"));
166        assert!(yaml.contains("limitations: Max 1TB per day"));
167    }
168
169    #[test]
170    fn test_export_asset_with_runtime() {
171        let asset = CADSAsset {
172            api_version: "v1.0".to_string(),
173            kind: CADSKind::Application,
174            id: "test-id".to_string(),
175            name: "Test App".to_string(),
176            version: "1.0.0".to_string(),
177            status: CADSStatus::Validated,
178            domain: None,
179            domain_id: None,
180            tags: vec![],
181            description: None,
182            runtime: Some(CADSRuntime {
183                environment: Some("kubernetes".to_string()),
184                endpoints: Some(vec!["https://api.example.com".to_string()]),
185                container: Some(CADSRuntimeContainer {
186                    image: Some("myapp:latest".to_string()),
187                }),
188                resources: Some(CADSRuntimeResources {
189                    cpu: Some("2".to_string()),
190                    memory: Some("4Gi".to_string()),
191                    gpu: None,
192                }),
193            }),
194            sla: None,
195            pricing: None,
196            team: None,
197            risk: None,
198            compliance: None,
199            validation_profiles: None,
200            bpmn_models: None,
201            dmn_models: None,
202            openapi_specs: None,
203            custom_properties: None,
204            created_at: None,
205            updated_at: None,
206        };
207
208        let yaml = CADSExporter::export_asset(&asset);
209        assert!(yaml.contains("runtime:"));
210        assert!(yaml.contains("environment: kubernetes"));
211        assert!(yaml.contains("container:"));
212        assert!(yaml.contains("image: myapp:latest"));
213    }
214
215    #[test]
216    fn test_export_asset_with_team() {
217        let asset = CADSAsset {
218            api_version: "v1.0".to_string(),
219            kind: CADSKind::AIModel,
220            id: "test-id".to_string(),
221            name: "Test Model".to_string(),
222            version: "1.0.0".to_string(),
223            status: CADSStatus::Production,
224            domain: None,
225            domain_id: None,
226            tags: vec![],
227            description: None,
228            runtime: None,
229            sla: None,
230            pricing: None,
231            team: Some(vec![CADSTeamMember {
232                role: "owner".to_string(),
233                name: "John Doe".to_string(),
234                contact: Some("user@example.com".to_string()),
235            }]),
236            risk: None,
237            compliance: None,
238            validation_profiles: None,
239            bpmn_models: None,
240            dmn_models: None,
241            openapi_specs: None,
242            custom_properties: None,
243            created_at: None,
244            updated_at: None,
245        };
246
247        let yaml = CADSExporter::export_asset(&asset);
248        assert!(yaml.contains("team:"));
249        assert!(yaml.contains("name: John Doe"));
250        assert!(yaml.contains("role: owner"));
251    }
252
253    #[test]
254    fn test_export_all_kinds() {
255        let kinds = vec![
256            (CADSKind::AIModel, "AIModel"),
257            (CADSKind::MLPipeline, "MLPipeline"),
258            (CADSKind::Application, "Application"),
259            (CADSKind::ETLPipeline, "ETLPipeline"),
260            (CADSKind::SourceSystem, "SourceSystem"),
261            (CADSKind::DestinationSystem, "DestinationSystem"),
262            (CADSKind::DataPipeline, "DataPipeline"),
263            (CADSKind::ETLProcess, "ETLProcess"),
264        ];
265
266        for (kind, expected_str) in kinds {
267            let asset = CADSAsset {
268                api_version: "v1.0".to_string(),
269                kind,
270                id: "test-id".to_string(),
271                name: "Test".to_string(),
272                version: "1.0.0".to_string(),
273                status: CADSStatus::Draft,
274                domain: None,
275                domain_id: None,
276                tags: vec![],
277                description: None,
278                runtime: None,
279                sla: None,
280                pricing: None,
281                team: None,
282                risk: None,
283                compliance: None,
284                validation_profiles: None,
285                bpmn_models: None,
286                dmn_models: None,
287                openapi_specs: None,
288                custom_properties: None,
289                created_at: None,
290                updated_at: None,
291            };
292
293            let yaml = CADSExporter::export_asset(&asset);
294            assert!(
295                yaml.contains(&format!("kind: {}", expected_str)),
296                "Expected kind '{}' not found in YAML",
297                expected_str
298            );
299        }
300    }
301}