1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
use crate::{
    core::writers::{csv_writer, json_writer, sqlite_writer},
    helpers::file_system_helpers::{ensure_folder_exists, WORKSPACE_PATHS},
    models::Machine,
    progress::ProgressCallback,
};
use std::{
    collections::HashMap,
    error::Error,
    fmt,
    path::{Path, PathBuf},
};

/// Writes machine data to the specified export file type.
///
/// This function handles the export of machine data to the chosen format (`SQLite`, `JSON`, or `CSV`)
/// by creating the necessary export folder in the workspace path and invoking the appropriate writer function.
/// It ensures that the target directory exists, then delegates the writing task to the relevant module
/// based on the selected `ExportFileType`. Progress updates and messages are provided via a callback function.
///
/// # Parameters
/// - `export_file_type`: An `ExportFileType` enum specifying the format for data export. Supported types are:
///   - `ExportFileType::Sqlite`: Exports data to a SQLite database file.
///   - `ExportFileType::Json`: Exports data to a JSON file.
///   - `ExportFileType::Csv`: Exports data to a CSV file.
/// - `workspace_path`: A reference to a `Path` representing the base directory where the exported files will be stored.
/// - `machines`: A reference to a `HashMap` where keys are machine names and values are `Machine` structs containing
///   detailed information about each MAME machine.
/// - `progress_callback`: A callback function of type `ProgressCallback` that provides status updates and progress
///   information during the export process. The callback receives a `ProgressInfo` struct containing `progress`, `total`,
///   `message`, and `callback_type`.
///
/// # Returns
/// Returns a `Result<PathBuf, Box<dyn Error + Send + Sync>>`:
/// - On success: Contains a `PathBuf` representing the path to the folder where the export files are stored.
/// - On failure: Contains an error if the export folder cannot be created or if there is an issue during the writing process.
///
/// # Errors
/// This function will return an error if:
/// - The export folder cannot be created due to permission issues or file system errors.
/// - The writing process fails for the selected export file type due to data formatting issues or I/O errors.
///
/// # Callback
/// The progress callback function provides real-time updates on the export process. It receives:
/// - `progress`: The current progress of the export operation (e.g., number of records processed).
/// - `total`: The total number of items to be exported.
/// - `message`: A status message indicating the current operation (e.g., "Creating export folder", "Writing to file").
/// - `callback_type`: The type of callback, such as `CallbackType::Info`, `CallbackType::Error`, `CallbackType::Progress`, or `CallbackType::Finish`.
///
/// # Example Sqlite
#[doc = docify::embed!("examples/write_sqlite.rs", main)]
///
/// # Example Csv
#[doc = docify::embed!("examples/write_csv.rs", main)]
///
/// # Example Json
#[doc = docify::embed!("examples/write_json.rs", main)]
///
pub fn write_files(
    export_file_type: ExportFileType,
    workspace_path: &Path,
    machines: &HashMap<String, Machine>,
    progress_callback: ProgressCallback,
) -> Result<PathBuf, Box<dyn Error + Send + Sync>> {
    let export_folder = workspace_path
        .join(WORKSPACE_PATHS.export_path)
        .join(export_file_type.to_string().to_lowercase());

    let folder_created = ensure_folder_exists(&export_folder);
    if let Err(err) = folder_created {
        return Err(Box::new(err));
    }

    match export_file_type {
        ExportFileType::Sqlite => {
            let data_base_path = export_folder.join("machines.db");
            sqlite_writer::write_sqlite(
                &data_base_path.to_string_lossy(),
                &machines,
                progress_callback,
            )?;
        }
        ExportFileType::Json => {
            json_writer::write_json(
                &export_folder.to_string_lossy(),
                &machines,
                progress_callback,
            )?;
        }
        ExportFileType::Csv => {
            csv_writer::write_csv(
                &export_folder.to_string_lossy(),
                &machines,
                progress_callback,
            )?;
        }
    }

    Ok(export_folder)
}

/// Represents the file type to be used for data export.
///
/// The `ExportFileType` enum defines the different formats supported for exporting data,
/// allowing the caller to choose between database, structured data, and tabular formats.
/// This is particularly useful in scenarios where data needs to be shared, stored, or analyzed
/// in various ways.
///
/// # Variants
/// - `Sqlite`: Exports the data to a SQLite database file, suitable for structured storage and complex queries.
/// - `Json`: Exports the data to a JSON (JavaScript Object Notation) file, ideal for web applications and data interchange.
/// - `Csv`: Exports the data to a CSV (Comma-Separated Values) file, useful for spreadsheet applications and basic data analysis.
///
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExportFileType {
    /// Exports data to a SQLite database file.
    Sqlite,
    /// Exports data to a JSON file.
    Json,
    /// Exports data to a CSV file.
    Csv,
}

/// Implements the `fmt::Display` trait for `ExportFileType`.
///
/// This allows instances of `ExportFileType` to be formatted as strings,
/// making it easy to display or print the enum values in a user-friendly way.
impl fmt::Display for ExportFileType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        // Match each variant to its corresponding string representation
        let as_str = match self {
            ExportFileType::Sqlite => "sqlite",
            ExportFileType::Json => "json",
            ExportFileType::Csv => "csv",
        };
        // Write the string representation to the formatter
        write!(f, "{}", as_str)
    }
}