sofie_db/migrate/
status.rs1use sofie_core::Result;
2
3use super::{Applied, Migrations, Schema, applied_migrations, checksum, name_order};
4use crate::{Engine, Step};
5
6#[derive(Clone, Copy, Debug, PartialEq, Eq)]
7pub enum MigrationState {
8 Applied,
9 Pending,
10 EditedAfterApplied,
11 MissingFile,
12}
13
14#[derive(Clone, Debug, PartialEq, Eq)]
15pub struct MigrationStatus {
16 pub name: String,
17 pub state: MigrationState,
18}
19
20#[derive(Clone, Debug, PartialEq, Eq)]
21pub struct ModuleStatus {
22 pub module: String,
23 pub migrations: Vec<MigrationStatus>,
24}
25
26impl ModuleStatus {
27 #[must_use]
28 pub fn is_consistent(&self) -> bool {
29 self.migrations.iter().all(|migration| {
30 matches!(
31 migration.state,
32 MigrationState::Applied | MigrationState::Pending
33 )
34 })
35 }
36}
37
38impl Schema<'_> {
39 pub async fn migration_status(self, modules: &[Migrations]) -> Result<Vec<ModuleStatus>> {
41 let dialect = self.db.driver().dialect();
42 self.db
43 .transaction(vec![Step::Script(dialect.migrations_setup().to_owned())])
44 .await?;
45 let mut applied = applied_migrations(self.db).await?;
46 let framework = dialect.framework_migrations();
47 let mut statuses = std::iter::once(&framework)
48 .chain(modules)
49 .map(|migrations| module_status(migrations, dialect.engine(), &mut applied))
50 .collect::<Result<Vec<ModuleStatus>>>()?;
51 statuses.extend(unknown_modules(applied));
52 Ok(statuses)
53 }
54}
55
56fn module_status(
57 migrations: &Migrations,
58 engine: Engine,
59 applied: &mut Applied,
60) -> Result<ModuleStatus> {
61 let module = migrations.module.to_owned();
62 let mut statuses: Vec<MigrationStatus> = migrations
63 .in_name_order(engine)?
64 .into_iter()
65 .map(|(name, sql)| {
66 let recorded = applied.remove(&(module.clone(), name.to_owned()));
67 let state = match recorded {
68 None => MigrationState::Pending,
69 Some(sum) if sum == checksum(&sql) => MigrationState::Applied,
70 Some(_) => MigrationState::EditedAfterApplied,
71 };
72 MigrationStatus {
73 name: name.to_owned(),
74 state,
75 }
76 })
77 .collect();
78 let missing = applied.extract_if(|(recorded_module, _), _| *recorded_module == module);
79 statuses.extend(missing.map(|((_, name), _)| MigrationStatus {
80 name,
81 state: MigrationState::MissingFile,
82 }));
83 statuses.sort_by(|first, second| name_order(&first.name, &second.name));
84 Ok(ModuleStatus {
85 module,
86 migrations: statuses,
87 })
88}
89
90fn unknown_modules(applied: Applied) -> Vec<ModuleStatus> {
91 let mut records: Vec<(String, String)> = applied.into_keys().collect();
92 records.sort();
93 let mut statuses: Vec<ModuleStatus> = Vec::new();
94 for (module, name) in records {
95 let missing = MigrationStatus {
96 name,
97 state: MigrationState::MissingFile,
98 };
99 match statuses.last_mut() {
100 Some(last) if last.module == module => last.migrations.push(missing),
101 _ => statuses.push(ModuleStatus {
102 module,
103 migrations: vec![missing],
104 }),
105 }
106 }
107 statuses
108}