build.rs 4.6 KB

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