use std::fs::{self, File}; use std::io::Write; use std::path::{Path, PathBuf}; fn main() { // Step 1: Find `migrations/` folder recursively let root = Path::new("src"); for migration_path in find_migrations_dirs(root) { // Step 2: Collect all files inside the migrations dir let mut files = Vec::new(); visit_dirs(&migration_path, &mut files).expect("Failed to read migrations directory"); files.sort_by(|path_a, path_b| { let path_a = path_a.file_name().unwrap().to_str().unwrap(); let path_b = path_b.file_name().unwrap().to_str().unwrap(); let prefix_a = path_a .split("_") .next() .and_then(|prefix| prefix.parse::().ok()) .unwrap_or_default(); let prefix_b = path_b .split("_") .next() .and_then(|prefix| prefix.parse::().ok()) .unwrap_or_default(); if prefix_a != 0 && prefix_b != 0 { prefix_a.cmp(&prefix_b) } else { path_a.cmp(path_b) } }); // Step 3: Output file path (e.g., `src/db/migrations.rs`) let parent = migration_path.parent().unwrap(); let skip_path = parent.to_str().unwrap_or_default().len(); let dest_path = parent.join("migrations.rs"); let mut out_file = File::create(&dest_path).expect("Failed to create migrations.rs"); let skip_name = migration_path.to_str().unwrap_or_default().len(); writeln!(out_file, "/// @generated").unwrap(); writeln!(out_file, "/// Auto-generated by build.rs").unwrap(); writeln!( out_file, "pub static MIGRATIONS: &[(&str, &str, &str)] = &[" ) .unwrap(); for path in &files { let parts = path.to_str().unwrap().replace("\\", "/")[skip_name + 1..] .split("/") .map(|x| x.to_owned()) .collect::>(); let prefix = if parts.len() == 2 { parts.first().map(|x| x.to_owned()).unwrap_or_default() } else { "".to_owned() }; let rel_name = &path.file_name().unwrap().to_str().unwrap(); let rel_path = &path.to_str().unwrap().replace("\\", "/")[skip_path..]; // for Windows writeln!( out_file, " (\"{prefix}\", \"{rel_name}\", include_str!(r#\".{rel_path}\"#))," ) .unwrap(); println!("cargo:rerun-if-changed={}", path.display()); } writeln!(out_file, "];").unwrap(); println!("cargo:rerun-if-changed={}", migration_path.display()); } } fn find_migrations_dirs(root: &Path) -> Vec { let mut found = Vec::new(); find_migrations_dirs_rec(root, &mut found); found } fn find_migrations_dirs_rec(dir: &Path, found: &mut Vec) { if let Ok(entries) = fs::read_dir(dir) { for entry in entries.flatten() { let path = entry.path(); if path.is_dir() { if path.file_name().unwrap_or_default() == "migrations" { found.push(path.clone()); } find_migrations_dirs_rec(&path, found); } } } } fn visit_dirs(dir: &Path, files: &mut Vec) -> std::io::Result<()> { for entry in fs::read_dir(dir)? { let entry = entry?; let path = entry.path(); if path.is_dir() { visit_dirs(&path, files)?; } else if path.is_file() { files.push(path); } } Ok(()) }