drizzle_cli/commands/
export.rs1use std::path::PathBuf;
6
7use crate::commands::overrides;
8use crate::config::Config;
9use crate::config::Dialect;
10use crate::error::CliError;
11use crate::output;
12use drizzle_migrations::schema::Snapshot;
13
14#[derive(clap::Args, Debug, Clone)]
15pub struct ExportOptions {
16 #[arg(long = "sql")]
18 pub output_path: Option<PathBuf>,
19
20 #[arg(long)]
22 pub dialect: Option<Dialect>,
23
24 #[arg(long, value_delimiter = ',')]
26 pub schema: Option<Vec<String>>,
27}
28
29pub fn run(config: &Config, db_name: Option<&str>, opts: ExportOptions) -> Result<(), CliError> {
37 use drizzle_migrations::parser::SchemaParser;
38
39 let db = config.database(db_name)?;
40 let effective_dialect = overrides::resolve_dialect(db, opts.dialect);
41
42 crate::commands::harness::print_db_header(config, db_name);
43
44 println!("{}", output::heading("Exporting schema as SQL..."));
45 println!();
46
47 println!(
48 " {}: {}",
49 output::label("Dialect"),
50 effective_dialect.as_str()
51 );
52
53 let schema_files = overrides::resolve_schema_files(db, opts.schema.as_deref())?;
55 if schema_files.is_empty() {
56 return Err(CliError::NoSchemaFiles(overrides::resolve_schema_display(
57 db,
58 opts.schema.as_deref(),
59 )));
60 }
61
62 println!(
63 " {} {} schema file(s)",
64 output::label("Parsing"),
65 schema_files.len()
66 );
67
68 let mut combined_code = String::new();
69 for path in &schema_files {
70 let code = std::fs::read_to_string(path)
71 .map_err(|e| CliError::IoError(format!("Failed to read {}: {}", path.display(), e)))?;
72 combined_code.push_str(&code);
73 combined_code.push('\n');
74 }
75
76 let parse_result = SchemaParser::parse(&combined_code);
77 crate::snapshot::surface_parse_diagnostics(&parse_result)?;
78
79 if parse_result.tables.is_empty() && parse_result.indexes.is_empty() {
80 println!(
81 "{}",
82 output::warning("No tables or indexes found in schema files.")
83 );
84 return Ok(());
85 }
86
87 println!(
88 " {} {} table(s), {} index(es)",
89 output::label("Found"),
90 parse_result.tables.len(),
91 parse_result.indexes.len()
92 );
93
94 let dialect = effective_dialect.to_base();
96 let snapshot = Snapshot::from_parse_result(&parse_result, dialect, db.casing);
97
98 let sql_statements = generate_create_sql(&snapshot, db.breakpoints);
100
101 if sql_statements.is_empty() {
102 println!("{}", output::warning("No SQL statements generated."));
103 return Ok(());
104 }
105
106 let sql_content = sql_statements.join("\n\n");
107
108 if let Some(path) = opts.output_path {
110 std::fs::write(&path, &sql_content)
111 .map_err(|e| CliError::IoError(format!("Failed to write {}: {}", path.display(), e)))?;
112 println!();
113 println!(
114 "{}",
115 output::success(&format!(
116 "Exported {} SQL statement(s) to {}",
117 sql_statements.len(),
118 path.display()
119 ))
120 );
121 } else {
122 println!();
123 println!("{}", output::muted("-- Generated SQL --"));
124 println!();
125 println!("{sql_content}");
126 println!();
127 println!("{}", output::muted("-- End of SQL --"));
128 }
129
130 Ok(())
131}
132
133fn generate_create_sql(
135 snapshot: &drizzle_migrations::schema::Snapshot,
136 breakpoints: bool,
137) -> Vec<String> {
138 match snapshot {
139 Snapshot::Sqlite(snap) => {
140 use drizzle_migrations::sqlite::SQLiteSnapshot;
141 use drizzle_migrations::sqlite::diff_snapshots;
142 use drizzle_migrations::sqlite::statements::Generator as SqliteGenerator;
143
144 let empty = SQLiteSnapshot::new();
146 let diff = diff_snapshots(&empty, snap);
147 let generator = SqliteGenerator::new().with_breakpoints(breakpoints);
148 generator.generate_migration(&diff)
149 }
150 Snapshot::Postgres(snap) => {
151 use drizzle_migrations::postgres::PostgresSnapshot;
152 use drizzle_migrations::postgres::diff_full_snapshots;
153 use drizzle_migrations::postgres::statements::Generator as PostgresGenerator;
154
155 let empty = PostgresSnapshot::new();
157 let diff = diff_full_snapshots(&empty, snap);
158 let generator = PostgresGenerator::new().with_breakpoints(breakpoints);
159 generator.generate(&diff.diffs)
160 }
161 }
162}