drizzle_cli/commands/
export.rs

1//! Export command implementation
2//!
3//! Exports the schema as SQL statements.
4
5use std::path::PathBuf;
6
7use crate::config::DrizzleConfig;
8use crate::error::CliError;
9use crate::output;
10use crate::snapshot::parse_result_to_snapshot;
11
12/// Run the export command
13pub fn run(
14    config: &DrizzleConfig,
15    db_name: Option<&str>,
16    output_path: Option<PathBuf>,
17) -> Result<(), CliError> {
18    use drizzle_migrations::parser::SchemaParser;
19
20    let db = config.database(db_name)?;
21
22    if !config.is_single_database() {
23        let name = db_name.unwrap_or("(default)");
24        println!("{}: {}", output::label("Database"), name);
25    }
26
27    println!("{}", output::heading("Exporting schema as SQL..."));
28    println!();
29
30    // Parse schema files
31    let schema_files = db.schema_files()?;
32    if schema_files.is_empty() {
33        return Err(CliError::NoSchemaFiles(db.schema_display()));
34    }
35
36    println!(
37        "  {} {} schema file(s)",
38        output::label("Parsing"),
39        schema_files.len()
40    );
41
42    let mut combined_code = String::new();
43    for path in &schema_files {
44        let code = std::fs::read_to_string(path)
45            .map_err(|e| CliError::IoError(format!("Failed to read {}: {}", path.display(), e)))?;
46        combined_code.push_str(&code);
47        combined_code.push('\n');
48    }
49
50    let parse_result = SchemaParser::parse(&combined_code);
51
52    if parse_result.tables.is_empty() && parse_result.indexes.is_empty() {
53        println!(
54            "{}",
55            output::warning("No tables or indexes found in schema files.")
56        );
57        return Ok(());
58    }
59
60    println!(
61        "  {} {} table(s), {} index(es)",
62        output::label("Found"),
63        parse_result.tables.len(),
64        parse_result.indexes.len()
65    );
66
67    // Build snapshot from parsed schema (use config dialect)
68    let dialect = db.dialect.to_base();
69    let snapshot = parse_result_to_snapshot(&parse_result, dialect);
70
71    // Generate SQL from snapshot (create statements for all entities)
72    let sql_statements = generate_create_sql(&snapshot, db.breakpoints)?;
73
74    if sql_statements.is_empty() {
75        println!("{}", output::warning("No SQL statements generated."));
76        return Ok(());
77    }
78
79    let sql_content = sql_statements.join("\n\n");
80
81    // Output to file or stdout
82    match output_path {
83        Some(path) => {
84            std::fs::write(&path, &sql_content).map_err(|e| {
85                CliError::IoError(format!("Failed to write {}: {}", path.display(), e))
86            })?;
87            println!();
88            println!(
89                "{}",
90                output::success(&format!(
91                    "Exported {} SQL statement(s) to {}",
92                    sql_statements.len(),
93                    path.display()
94                ))
95            );
96        }
97        None => {
98            println!();
99            println!("{}", output::muted("-- Generated SQL --"));
100            println!();
101            println!("{}", sql_content);
102            println!();
103            println!("{}", output::muted("-- End of SQL --"));
104        }
105    }
106
107    Ok(())
108}
109
110/// Generate CREATE SQL statements from a snapshot
111fn generate_create_sql(
112    snapshot: &drizzle_migrations::schema::Snapshot,
113    breakpoints: bool,
114) -> Result<Vec<String>, CliError> {
115    use drizzle_migrations::schema::Snapshot;
116
117    match snapshot {
118        Snapshot::Sqlite(snap) => {
119            use drizzle_migrations::sqlite::SQLiteSnapshot;
120            use drizzle_migrations::sqlite::diff_snapshots;
121            use drizzle_migrations::sqlite::statements::SqliteGenerator;
122
123            // Diff against empty snapshot to get all CREATE statements
124            let empty = SQLiteSnapshot::new();
125            let diff = diff_snapshots(&empty, snap);
126            let generator = SqliteGenerator::new().with_breakpoints(breakpoints);
127            Ok(generator.generate_migration(&diff))
128        }
129        Snapshot::Postgres(snap) => {
130            use drizzle_migrations::postgres::PostgresSnapshot;
131            use drizzle_migrations::postgres::diff_full_snapshots;
132            use drizzle_migrations::postgres::statements::PostgresGenerator;
133
134            // Diff against empty snapshot to get all CREATE statements
135            let empty = PostgresSnapshot::new();
136            let diff = diff_full_snapshots(&empty, snap);
137            let generator = PostgresGenerator::new().with_breakpoints(breakpoints);
138            Ok(generator.generate(&diff.diffs))
139        }
140    }
141}