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 self.ensure_migrations_table_exists().await?;
44
45 let applied = self.get_applied_migrations(ext_id).await?;
46 if applied.iter().any(|m| m.version == version) {
47 return Err(LoaderError::MigrationFailed {
48 extension: ext_id.to_owned(),
49 message: format!(
50 "Migration {version} ('{}') is already tracked as applied for extension \
51 '{ext_id}'; nothing to do",
52 migration.name
53 ),
54 });
55 }
56
57 let id = format!("{ext_id}_{:03}", migration.version);
58 let checksum = migration.checksum();
59
60 self.db
61 .execute(
62 &"INSERT INTO extension_migrations (id, extension_id, version, name, checksum) \
63 VALUES ($1, $2, $3, $4, $5)",
64 &[&id, &ext_id, &migration.version, &migration.name, &checksum],
65 )
66 .await
67 .map_err(|e| LoaderError::MigrationFailed {
68 extension: ext_id.to_owned(),
69 message: format!("Failed to record migration as applied: {e}"),
70 })?;
71
72 Ok(MarkAppliedOutcome {
73 extension_id: ext_id.to_owned(),
74 version: migration.version,
75 name: migration.name.clone(),
76 checksum,
77 })
78 }
79}