123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162 |
- use std::cmp::Ordering;
- use std::env;
- 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");
- // Get the OUT_DIR from Cargo - this is writable
- let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR is set by Cargo"));
- for migration_path in find_migrations_dirs(root) {
- // Step 3: Output file path to OUT_DIR instead of source directory
- let parent = migration_path.parent().unwrap();
- // Create a unique filename based on the migration path to avoid conflicts
- let migration_name = parent
- .strip_prefix("src")
- .unwrap_or(parent)
- .to_str()
- .unwrap_or("default")
- .replace("/", "_")
- .replace("\\", "_");
- let dest_path = out_dir.join(format!("migrations_{}.rs", migration_name));
- 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();
- // 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 parts_a = path_a.to_str().unwrap().replace("\\", "/")[skip_name + 1..]
- .split("/")
- .map(|x| x.to_owned())
- .collect::<Vec<_>>();
- let parts_b = path_b.to_str().unwrap().replace("\\", "/")[skip_name + 1..]
- .split("/")
- .map(|x| x.to_owned())
- .collect::<Vec<_>>();
- let prefix_a = if parts_a.len() == 2 {
- parts_a.first().map(|x| x.to_owned()).unwrap_or_default()
- } else {
- "".to_owned()
- };
- let prefix_b = if parts_a.len() == 2 {
- parts_b.first().map(|x| x.to_owned()).unwrap_or_default()
- } else {
- "".to_owned()
- };
- let prefix_cmp = prefix_a.cmp(&prefix_b);
- if prefix_cmp != Ordering::Equal {
- return prefix_cmp;
- }
- 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::<usize>().ok())
- .unwrap_or_default();
- let prefix_b = path_b
- .split("_")
- .next()
- .and_then(|prefix| prefix.parse::<usize>().ok())
- .unwrap_or_default();
- if prefix_a != 0 && prefix_b != 0 {
- prefix_a.cmp(&prefix_b)
- } else {
- path_a.cmp(path_b)
- }
- });
- 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::<Vec<_>>();
- 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();
- // Copy migration file to OUT_DIR
- let relative_path = path.strip_prefix(root).unwrap();
- let dest_migration_file = out_dir.join(relative_path);
- if let Some(parent) = dest_migration_file.parent() {
- fs::create_dir_all(parent)
- .expect("Failed to create migration directory in OUT_DIR");
- }
- fs::copy(path, &dest_migration_file).expect("Failed to copy migration file to OUT_DIR");
- // Use path relative to OUT_DIR for include_str
- let relative_to_out_dir = relative_path.to_str().unwrap().replace("\\", "/");
- writeln!(
- out_file,
- " (\"{prefix}\", \"{rel_name}\", include_str!(r#\"{}\"#)),",
- relative_to_out_dir
- )
- .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<PathBuf> {
- let mut found = Vec::new();
- find_migrations_dirs_rec(root, &mut found);
- found
- }
- fn find_migrations_dirs_rec(dir: &Path, found: &mut Vec<PathBuf>) {
- 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<PathBuf>) -> 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(())
- }
|