rust_bucket/
migrations.rs1use rust_embed::RustEmbed;
7use semver::Version;
8use thiserror::Error;
9
10#[derive(RustEmbed)]
12#[folder = "migrations/"]
13struct MigrationFiles;
14
15#[derive(Debug, Clone)]
17pub struct Migration {
18 pub version: Version,
19 pub instructions: String,
20}
21
22#[derive(Debug, Error)]
24pub enum MigrationError {
25 #[error("Failed to parse version '{0}': {1}")]
26 VersionParse(String, semver::Error),
27}
28
29pub fn migrations_between(from: &Version, to: &Version) -> Result<Vec<Migration>, MigrationError> {
36 if from >= to {
37 return Ok(Vec::new());
38 }
39
40 let mut migrations = Vec::new();
41
42 for filename in MigrationFiles::iter() {
43 let version_str = match filename.strip_suffix(".md") {
45 Some(v) => v,
46 None => continue,
47 };
48
49 let version = match Version::parse(version_str) {
51 Ok(v) => v,
52 Err(_) => continue,
53 };
54
55 if version > *from
57 && version <= *to
58 && let Some(file) = MigrationFiles::get(&filename)
59 {
60 let instructions = String::from_utf8_lossy(&file.data).to_string();
61 migrations.push(Migration {
62 version,
63 instructions,
64 });
65 }
66 }
67
68 migrations.sort_by(|a, b| a.version.cmp(&b.version));
70
71 Ok(migrations)
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77
78 #[test]
79 fn test_migrations_between_includes_060() {
80 let from = Version::new(0, 5, 0);
81 let to = Version::new(0, 6, 0);
82 let result = migrations_between(&from, &to).unwrap();
83 assert_eq!(result.len(), 1);
84 assert_eq!(result[0].version, Version::new(0, 6, 0));
85 }
86
87 #[test]
88 fn test_migrations_between_same_version_returns_empty() {
89 let v = Version::new(0, 6, 0);
90 let result = migrations_between(&v, &v).unwrap();
91 assert!(result.is_empty());
92 }
93
94 #[test]
95 fn test_migrations_between_reversed_returns_empty() {
96 let from = Version::new(0, 7, 0);
97 let to = Version::new(0, 5, 0);
98 let result = migrations_between(&from, &to).unwrap();
99 assert!(result.is_empty());
100 }
101
102 #[test]
103 fn test_migration_content_is_non_empty() {
104 let from = Version::new(0, 5, 0);
105 let to = Version::new(0, 6, 0);
106 let result = migrations_between(&from, &to).unwrap();
107 assert!(!result.is_empty());
108 for migration in &result {
109 assert!(!migration.instructions.is_empty());
110 }
111 }
112}