Skip to main content

gaman_core/
migrations.rs

1use std::collections::HashSet;
2
3use serde::{Deserialize, Serialize};
4
5use crate::operations::Operation;
6use crate::states::types::EntityKind;
7
8fn bool_true() -> bool {
9    true
10}
11
12/// A single migration: an ordered, dependency-aware set of operations.
13/// `id` is derived from the filename at load time and is not stored in the file.
14#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct Migration {
16    #[serde(skip)]
17    pub id: String,
18    #[serde(default, skip_serializing_if = "Vec::is_empty")]
19    pub dependencies: Vec<String>,
20    pub operations: Vec<Operation>,
21    /// When false, this migration runs outside a transaction. Required for
22    /// operations that cannot run inside a transaction (e.g. CREATE INDEX CONCURRENTLY).
23    /// Defaults to true so existing migration files are unaffected.
24    #[serde(default = "bool_true", skip_serializing_if = "std::ops::Not::not")]
25    pub atomic: bool,
26}
27
28impl Migration {
29    pub fn from_yaml_str(s: &str) -> Result<Self, serde_yaml::Error> {
30        serde_yaml::from_str(s)
31    }
32
33    pub fn to_yaml_string(&self) -> Result<String, serde_yaml::Error> {
34        serde_yaml::to_string(self)
35    }
36
37    /// Returns the set of top-level entities touched by this migration.
38    /// Includes FK target tables so callers can detect cross-namespace references.
39    pub fn get_entities(&self) -> HashSet<(EntityKind, String)> {
40        self.operations
41            .iter()
42            .flat_map(Operation::touched_entities)
43            .collect()
44    }
45}