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