build.rs 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. use std::fs::{self, File};
  2. use std::io::Write;
  3. use std::path::{Path, PathBuf};
  4. fn main() {
  5. // Step 1: Find `migrations/` folder recursively
  6. let root = Path::new("src");
  7. for migration_path in find_migrations_dirs(root) {
  8. // Step 2: Collect all files inside the migrations dir
  9. let mut files = Vec::new();
  10. visit_dirs(&migration_path, &mut files).expect("Failed to read migrations directory");
  11. files.sort_by(|path_a, path_b| {
  12. let path_a = path_a.file_name().unwrap().to_str().unwrap();
  13. let path_b = path_b.file_name().unwrap().to_str().unwrap();
  14. let prefix_a = path_a
  15. .split("_")
  16. .next()
  17. .and_then(|prefix| prefix.parse::<usize>().ok());
  18. let prefix_b = path_b
  19. .split("_")
  20. .next()
  21. .and_then(|prefix| prefix.parse::<usize>().ok());
  22. if prefix_a.is_some() && prefix_b.is_some() {
  23. prefix_a.unwrap().cmp(&prefix_b.unwrap())
  24. } else {
  25. path_a.cmp(&path_b)
  26. }
  27. });
  28. // Step 3: Output file path (e.g., `src/db/migrations.rs`)
  29. let parent = migration_path.parent().unwrap();
  30. let skip_path = parent.to_str().unwrap_or_default().len();
  31. let dest_path = parent.join("migrations.rs");
  32. let mut out_file = File::create(&dest_path).expect("Failed to create migrations.rs");
  33. let skip_name = migration_path.to_str().unwrap_or_default().len();
  34. writeln!(out_file, "/// @generated").unwrap();
  35. writeln!(out_file, "/// Auto-generated by build.rs").unwrap();
  36. writeln!(
  37. out_file,
  38. "pub static MIGRATIONS: &[(&str, &str, &str)] = &["
  39. )
  40. .unwrap();
  41. for path in &files {
  42. let parts = path.to_str().unwrap().replace("\\", "/")[skip_name + 1..]
  43. .split("/")
  44. .map(|x| x.to_owned())
  45. .collect::<Vec<_>>();
  46. let prefix = if parts.len() == 2 {
  47. parts.first().map(|x| x.to_owned()).unwrap_or_default()
  48. } else {
  49. "".to_owned()
  50. };
  51. let rel_name = &path.file_name().unwrap().to_str().unwrap();
  52. let rel_path = &path.to_str().unwrap().replace("\\", "/")[skip_path..]; // for Windows
  53. writeln!(
  54. out_file,
  55. " (\"{prefix}\", \"{rel_name}\", include_str!(r#\".{rel_path}\"#)),"
  56. )
  57. .unwrap();
  58. println!("cargo:rerun-if-changed={}", path.display());
  59. }
  60. writeln!(out_file, "];").unwrap();
  61. println!("cargo:rerun-if-changed={}", migration_path.display());
  62. }
  63. }
  64. fn find_migrations_dirs(root: &Path) -> Vec<PathBuf> {
  65. let mut found = Vec::new();
  66. find_migrations_dirs_rec(root, &mut found);
  67. found
  68. }
  69. fn find_migrations_dirs_rec(dir: &Path, found: &mut Vec<PathBuf>) {
  70. if let Ok(entries) = fs::read_dir(dir) {
  71. for entry in entries.flatten() {
  72. let path = entry.path();
  73. if path.is_dir() {
  74. if path.file_name().unwrap_or_default() == "migrations" {
  75. found.push(path.clone());
  76. }
  77. find_migrations_dirs_rec(&path, found);
  78. }
  79. }
  80. }
  81. }
  82. fn visit_dirs(dir: &Path, files: &mut Vec<PathBuf>) -> std::io::Result<()> {
  83. for entry in fs::read_dir(dir)? {
  84. let entry = entry?;
  85. let path = entry.path();
  86. if path.is_dir() {
  87. visit_dirs(&path, files)?;
  88. } else if path.is_file() {
  89. files.push(path);
  90. }
  91. }
  92. Ok(())
  93. }