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