systemprompt_database/lifecycle/migrations/
mark_applied.rs1use super::MigrationService;
14use systemprompt_extension::{Extension, LoaderError};
15
16#[derive(Debug, Clone)]
17pub struct MarkAppliedOutcome {
18 pub extension_id: String,
19 pub version: u32,
20 pub name: String,
21 pub checksum: String,
22}
23
24impl MigrationService<'_> {
25 pub async fn mark_applied(
26 &self,
27 extension: &dyn Extension,
28 version: u32,
29 ) -> Result<MarkAppliedOutcome, LoaderError> {
30 let ext_id = extension.metadata().id;
31
32 let migration = extension
33 .migrations()
34 .into_iter()
35 .find(|m| m.version == version)
36 .ok_or_else(|| LoaderError::MigrationFailed {
37 extension: ext_id.to_owned(),
38 message: format!(
39 "Migration version {version} is not defined for extension '{ext_id}'"
40 ),
41 })?;
42
43 if migration.tombstone {
44 return Err(LoaderError::MigrationFailed {
45 extension: ext_id.to_owned(),
46 message: format!(
47 "Migration {version} ('{}') is a tombstone: the slot is recorded as spent and \
48 has no SQL, so there is nothing to mark applied",
49 migration.name
50 ),
51 });
52 }
53
54 self.ensure_migrations_table_exists().await?;
55
56 let applied = self.get_applied_migrations(ext_id).await?;
57 if applied.iter().any(|m| m.version == version) {
58 return Err(LoaderError::MigrationFailed {
59 extension: ext_id.to_owned(),
60 message: format!(
61 "Migration {version} ('{}') is already tracked as applied for extension \
62 '{ext_id}'; nothing to do",
63 migration.name
64 ),
65 });
66 }
67
68 let id = format!("{ext_id}_{:03}", migration.version);
69 let checksum = migration.checksum();
70
71 self.db
72 .execute(
73 &"INSERT INTO extension_migrations (id, extension_id, version, name, checksum) \
74 VALUES ($1, $2, $3, $4, $5)",
75 &[&id, &ext_id, &migration.version, &migration.name, &checksum],
76 )
77 .await
78 .map_err(|e| LoaderError::MigrationFailed {
79 extension: ext_id.to_owned(),
80 message: format!("Failed to record migration as applied: {e}"),
81 })?;
82
83 Ok(MarkAppliedOutcome {
84 extension_id: ext_id.to_owned(),
85 version: migration.version,
86 name: migration.name.clone(),
87 checksum,
88 })
89 }
90}