data_modelling_core/export/
decision.rs

1//! Decision (MADR) exporter
2//!
3//! Exports Decision models to MADR-compliant YAML format.
4
5use crate::export::ExportError;
6use crate::models::decision::{Decision, DecisionIndex};
7
8/// Decision exporter for generating MADR-compliant YAML from Decision models
9pub struct DecisionExporter;
10
11impl DecisionExporter {
12    /// Create a new Decision exporter instance
13    pub fn new() -> Self {
14        Self
15    }
16
17    /// Export a decision to MADR YAML format
18    ///
19    /// # Arguments
20    ///
21    /// * `decision` - The Decision to export
22    ///
23    /// # Returns
24    ///
25    /// A Result containing the YAML string, or an ExportError
26    pub fn export(&self, decision: &Decision) -> Result<String, ExportError> {
27        let yaml = decision.to_yaml().map_err(|e| {
28            ExportError::SerializationError(format!("Failed to serialize decision: {}", e))
29        })?;
30
31        // Validate exported YAML against decision schema (if feature enabled)
32        #[cfg(feature = "schema-validation")]
33        {
34            use crate::validation::schema::validate_decision_internal;
35            validate_decision_internal(&yaml).map_err(ExportError::ValidationError)?;
36        }
37
38        Ok(yaml)
39    }
40
41    /// Export a decision without validation
42    ///
43    /// Use this when you want to skip schema validation for performance
44    /// or when exporting to a trusted destination.
45    pub fn export_without_validation(&self, decision: &Decision) -> Result<String, ExportError> {
46        decision.to_yaml().map_err(|e| {
47            ExportError::SerializationError(format!("Failed to serialize decision: {}", e))
48        })
49    }
50
51    /// Export a decisions index to YAML format
52    ///
53    /// # Arguments
54    ///
55    /// * `index` - The DecisionIndex to export
56    ///
57    /// # Returns
58    ///
59    /// A Result containing the YAML string, or an ExportError
60    pub fn export_index(&self, index: &DecisionIndex) -> Result<String, ExportError> {
61        index.to_yaml().map_err(|e| {
62            ExportError::SerializationError(format!("Failed to serialize decision index: {}", e))
63        })
64    }
65
66    /// Export multiple decisions to a directory
67    ///
68    /// # Arguments
69    ///
70    /// * `decisions` - The decisions to export
71    /// * `dir_path` - Directory to export to
72    /// * `workspace_name` - Workspace name for filename generation
73    ///
74    /// # Returns
75    ///
76    /// A Result with the number of files exported, or an ExportError
77    pub fn export_to_directory(
78        &self,
79        decisions: &[Decision],
80        dir_path: &std::path::Path,
81        workspace_name: &str,
82    ) -> Result<usize, ExportError> {
83        // Create directory if it doesn't exist
84        if !dir_path.exists() {
85            std::fs::create_dir_all(dir_path)
86                .map_err(|e| ExportError::IoError(format!("Failed to create directory: {}", e)))?;
87        }
88
89        let mut count = 0;
90        for decision in decisions {
91            let filename = decision.filename(workspace_name);
92            let path = dir_path.join(&filename);
93            let yaml = self.export(decision)?;
94            std::fs::write(&path, yaml).map_err(|e| {
95                ExportError::IoError(format!("Failed to write {}: {}", filename, e))
96            })?;
97            count += 1;
98        }
99
100        Ok(count)
101    }
102}
103
104impl Default for DecisionExporter {
105    fn default() -> Self {
106        Self::new()
107    }
108}
109
110#[cfg(test)]
111mod tests {
112    use super::*;
113    use crate::models::decision::{DecisionCategory, DecisionStatus};
114
115    #[test]
116    fn test_export_decision() {
117        let decision = Decision::new(
118            1,
119            "Use ODCS Format",
120            "We need a standard format.",
121            "Use ODCS v3.1.0.",
122        )
123        .with_status(DecisionStatus::Accepted)
124        .with_category(DecisionCategory::DataDesign);
125
126        let exporter = DecisionExporter::new();
127        let result = exporter.export_without_validation(&decision);
128        assert!(result.is_ok());
129        let yaml = result.unwrap();
130        assert!(yaml.contains("title: Use ODCS Format"));
131        assert!(yaml.contains("status: accepted"));
132    }
133
134    #[test]
135    fn test_export_decision_index() {
136        let index = DecisionIndex::new();
137        let exporter = DecisionExporter::new();
138        let result = exporter.export_index(&index);
139        assert!(result.is_ok());
140        let yaml = result.unwrap();
141        assert!(yaml.contains("schema_version"));
142        assert!(yaml.contains("next_number: 1"));
143    }
144}