Download cloud-forge/crates/cloud-migration/src/lib.rs from Snapkitty/tlm-jxcl-forge: direct link, hf CLI and curl.
- Browser
- Download file 4.29 kB
-
https://huggingface.co/Snapkitty/tlm-jxcl-forge/resolve/main/cloud-forge/crates/cloud-migration/src/lib.rs
- Command line
-
hf download hf://Snapkitty/tlm-jxcl-forge/cloud-forge/crates/cloud-migration/src/lib.rs
-
curl -L -o lib.rs https://huggingface.co/Snapkitty/tlm-jxcl-forge/resolve/main/cloud-forge/crates/cloud-migration/src/lib.rs
4.29 kB
| // Copyright (C) 2026 ERRANT DIGITAL INSTITUTE OF TECHNOLOGY (SEIT NGO) % SNAPKITTY COLLECTIVE | |
| // SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Commercial | |
| // See LICENSE-AGPL and LICENSE-COMMERCIAL in this repository for license terms. | |
| //! Enforces that a database-shaped resource's schema migrations apply | |
| //! strictly sequentially and without gaps. | |
| //! | |
| //! A [`MigrationLedger`] doesn't run migrations -- it has no idea what | |
| //! SQL or transformation a version number corresponds to -- it owns | |
| //! exactly one invariant: version `N` can only be recorded as applied | |
| //! once version `N-1` already is, starting from `1`. Skipping ahead, | |
| //! re-applying an already-applied version, and applying version `0` | |
| //! are all rejected the same way, and a rejection never mutates the | |
| //! ledger. | |
| use cloud_errors::CloudError; | |
| /// The sequence of schema-migration versions applied so far. | |
| pub struct MigrationLedger { | |
| applied: Vec<u32>, | |
| } | |
| impl MigrationLedger { | |
| pub fn new() -> Self { | |
| MigrationLedger { | |
| applied: Vec::new(), | |
| } | |
| } | |
| /// The highest applied migration version, or `0` if none have | |
| /// been applied yet. | |
| pub fn current_version(&self) -> u32 { | |
| self.applied.last().copied().unwrap_or(0) | |
| } | |
| /// Records `version` as applied. Rejected (leaving the ledger | |
| /// completely unchanged) unless `version` is exactly one more | |
| /// than [`Self::current_version`]. | |
| pub fn apply(&mut self, version: u32) -> Result<(), CloudError> { | |
| let expected = self.current_version() + 1; | |
| if version != expected { | |
| return Err(CloudError::InvalidTransition { | |
| what: "migration", | |
| from: self.current_version().to_string(), | |
| to: version.to_string(), | |
| }); | |
| } | |
| self.applied.push(version); | |
| Ok(()) | |
| } | |
| pub fn is_applied(&self, version: u32) -> bool { | |
| self.applied.contains(&version) | |
| } | |
| /// Every applied version, in the (necessarily increasing) order | |
| /// it was applied. | |
| pub fn applied_versions(&self) -> &[u32] { | |
| &self.applied | |
| } | |
| } | |
| mod tests { | |
| use super::*; | |
| fn a_new_ledger_starts_at_version_zero() { | |
| let ledger = MigrationLedger::new(); | |
| assert_eq!(ledger.current_version(), 0); | |
| assert!(ledger.applied_versions().is_empty()); | |
| } | |
| fn migrations_must_start_at_version_one() { | |
| let mut ledger = MigrationLedger::new(); | |
| assert!(ledger.apply(2).is_err()); | |
| assert_eq!(ledger.current_version(), 0); | |
| ledger.apply(1).unwrap(); | |
| assert_eq!(ledger.current_version(), 1); | |
| } | |
| fn sequential_migrations_apply_in_order() { | |
| let mut ledger = MigrationLedger::new(); | |
| ledger.apply(1).unwrap(); | |
| ledger.apply(2).unwrap(); | |
| ledger.apply(3).unwrap(); | |
| assert_eq!(ledger.current_version(), 3); | |
| assert_eq!(ledger.applied_versions(), &[1, 2, 3]); | |
| } | |
| fn skipping_a_version_is_rejected() { | |
| let mut ledger = MigrationLedger::new(); | |
| ledger.apply(1).unwrap(); | |
| let err = ledger.apply(3).unwrap_err(); | |
| assert!(matches!(err, CloudError::InvalidTransition { .. })); | |
| assert_eq!(ledger.current_version(), 1); | |
| } | |
| fn reapplying_an_already_applied_version_is_rejected() { | |
| let mut ledger = MigrationLedger::new(); | |
| ledger.apply(1).unwrap(); | |
| let err = ledger.apply(1).unwrap_err(); | |
| assert!(matches!(err, CloudError::InvalidTransition { .. })); | |
| assert_eq!(ledger.applied_versions(), &[1]); | |
| } | |
| fn a_rejected_apply_leaves_the_ledger_completely_unchanged() { | |
| let mut ledger = MigrationLedger::new(); | |
| ledger.apply(1).unwrap(); | |
| ledger.apply(2).unwrap(); | |
| assert!(ledger.apply(10).is_err()); | |
| assert_eq!(ledger.current_version(), 2); | |
| assert_eq!(ledger.applied_versions(), &[1, 2]); | |
| } | |
| fn is_applied_reflects_ledger_state() { | |
| let mut ledger = MigrationLedger::new(); | |
| ledger.apply(1).unwrap(); | |
| assert!(ledger.is_applied(1)); | |
| assert!(!ledger.is_applied(2)); | |
| } | |
| } | |