sql_cli/ui/operations/
data_export_operations.rs

1//! Data export operations
2//!
3//! This module contains operations for exporting data to various formats,
4//! extracted from the monolithic TUI to improve maintainability and testability.
5
6use crate::data::data_exporter::DataExporter;
7use crate::data::data_provider::DataProvider;
8
9/// Context for data export operations
10/// Provides the minimal interface needed for export operations without coupling to the full TUI
11pub struct DataExportContext<'a> {
12    pub data_provider: Option<Box<dyn DataProvider + 'a>>,
13}
14
15/// Result of a data export operation
16#[derive(Debug)]
17pub enum ExportResult {
18    Success(String),
19    Error(anyhow::Error),
20}
21
22impl ExportResult {
23    /// Apply the result to a status handler (success message or error)
24    pub fn apply_to_status<F, G>(self, set_status: F, set_error: G)
25    where
26        F: FnOnce(String),
27        G: FnOnce(&str, anyhow::Error),
28    {
29        match self {
30            ExportResult::Success(message) => set_status(message),
31            ExportResult::Error(e) => set_error("Export failed", e),
32        }
33    }
34}
35
36/// Export data to CSV format
37#[must_use]
38pub fn export_to_csv(ctx: &DataExportContext) -> ExportResult {
39    let result = if let Some(ref provider) = ctx.data_provider {
40        DataExporter::export_provider_to_csv(provider.as_ref())
41    } else {
42        Err(anyhow::anyhow!("No data available to export"))
43    };
44
45    match result {
46        Ok(message) => ExportResult::Success(message),
47        Err(e) => ExportResult::Error(e),
48    }
49}
50
51/// Export data to JSON format
52#[must_use]
53pub fn export_to_json(ctx: &DataExportContext) -> ExportResult {
54    let result = if let Some(ref provider) = ctx.data_provider {
55        DataExporter::export_provider_to_json(provider.as_ref())
56    } else {
57        Err(anyhow::anyhow!("No data available to export"))
58    };
59
60    match result {
61        Ok(message) => ExportResult::Success(message),
62        Err(e) => ExportResult::Error(e),
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69    use crate::data::data_provider::DataProvider;
70
71    // Mock data provider for testing
72    #[derive(Debug)]
73    struct MockDataProvider {
74        should_fail: bool,
75    }
76
77    impl DataProvider for MockDataProvider {
78        fn get_row(&self, _index: usize) -> Option<Vec<String>> {
79            None
80        }
81
82        fn get_column_count(&self) -> usize {
83            0
84        }
85        fn get_row_count(&self) -> usize {
86            0
87        }
88        fn get_column_names(&self) -> Vec<String> {
89            vec![]
90        }
91    }
92
93    #[test]
94    fn test_export_csv_no_provider() {
95        let ctx = DataExportContext {
96            data_provider: None,
97        };
98
99        let result = export_to_csv(&ctx);
100        match result {
101            ExportResult::Error(e) => assert_eq!(e.to_string(), "No data available to export"),
102            _ => panic!("Expected error for no provider"),
103        }
104    }
105
106    #[test]
107    fn test_export_json_no_provider() {
108        let ctx = DataExportContext {
109            data_provider: None,
110        };
111
112        let result = export_to_json(&ctx);
113        match result {
114            ExportResult::Error(e) => assert_eq!(e.to_string(), "No data available to export"),
115            _ => panic!("Expected error for no provider"),
116        }
117    }
118
119    #[test]
120    fn test_export_result_apply_success() {
121        let result = ExportResult::Success("Export completed".to_string());
122        let mut status_set = false;
123        let mut error_set = false;
124
125        result.apply_to_status(
126            |msg| {
127                assert_eq!(msg, "Export completed");
128                status_set = true;
129            },
130            |_prefix, _error| {
131                error_set = true;
132            },
133        );
134
135        assert!(status_set);
136        assert!(!error_set);
137    }
138
139    #[test]
140    fn test_export_result_apply_error() {
141        let result = ExportResult::Error(anyhow::anyhow!("Test error"));
142        let mut status_set = false;
143        let mut error_set = false;
144
145        result.apply_to_status(
146            |_msg| {
147                status_set = true;
148            },
149            |prefix, error| {
150                assert_eq!(prefix, "Export failed");
151                assert_eq!(error.to_string(), "Test error");
152                error_set = true;
153            },
154        );
155
156        assert!(!status_set);
157        assert!(error_set);
158    }
159}