drizzle_cli/commands/
export.rs1use std::path::PathBuf;
6
7use crate::config::DrizzleConfig;
8use crate::error::CliError;
9use crate::output;
10use crate::snapshot::parse_result_to_snapshot;
11
12pub 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 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 let dialect = db.dialect.to_base();
69 let snapshot = parse_result_to_snapshot(&parse_result, dialect);
70
71 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 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
110fn 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 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 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}