kmp_embedded/migration.rs
1//! Store migration, composed.
2//!
3//! The machinery lives in the storage adapter; what belongs here is the one
4//! decision it deliberately does not make — how a context event becomes
5//! projection mutations. That derivation is the application's, and injecting
6//! it here is what keeps the adapter free of the layer above it and the MCP
7//! binary free of the layer below.
8
9use std::path::Path;
10
11use kmp_adapter_embedded::{EmbeddedKernelStore, StorageEngine, StoreMigrationReceipt};
12use kmp_domain::PortError;
13
14/// Migrates `source_dir` into `destination_dir`, returning what was moved.
15/// The destination is created with the default engine.
16///
17/// The source is never opened for writing and its bytes are verified
18/// unchanged when the migration finishes; the destination must not already
19/// hold a store.
20pub async fn migrate_data_dir(
21 source_dir: &Path,
22 destination_dir: &Path,
23) -> Result<StoreMigrationReceipt, PortError> {
24 migrate_data_dir_to(source_dir, destination_dir, StorageEngine::Redb).await
25}
26
27/// [`migrate_data_dir`] with the destination engine chosen — how a store
28/// changes engines (ADR-018). A redb store becomes a SQLite one that two
29/// agent hosts can share by replaying its history into a fresh directory;
30/// the source stays as it was and the receipt records both layouts.
31pub async fn migrate_data_dir_to(
32 source_dir: &Path,
33 destination_dir: &Path,
34 destination_engine: StorageEngine,
35) -> Result<StoreMigrationReceipt, PortError> {
36 crate::data_dir::ensure_data_dir_skeleton(destination_dir)?;
37 let (_store, receipt) = EmbeddedKernelStore::migrate_data_dir_to(
38 source_dir,
39 destination_dir,
40 destination_engine,
41 kmp_application::projection_mutations_for_context_event,
42 )
43 .await?;
44 Ok(receipt)
45}
46
47/// Migrate once, reopen afterwards. Safe on every start: a destination that
48/// already holds a store is opened as it is, and the returned receipt says
49/// whether this call was the one that migrated it.
50pub async fn open_or_migrate_data_dir(
51 source_dir: &Path,
52 destination_dir: &Path,
53) -> Result<Option<StoreMigrationReceipt>, PortError> {
54 crate::data_dir::ensure_data_dir_skeleton(destination_dir)?;
55 let (_store, receipt) = EmbeddedKernelStore::open_or_migrate_data_dir(
56 source_dir,
57 destination_dir,
58 kmp_application::projection_mutations_for_context_event,
59 )
60 .await?;
61 Ok(receipt)
62}