mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
- Run de/ser roundtrip in pre_init even when db version matches, ensuring all #[serde(default)] fields are populated before any typed access - Add patchdb.md documentation for TypedDbWatch patterns - Update TS bindings for CheckPortParams, CheckPortRes, ifconfigUrl - Update CLAUDE.md docs with patchdb and component-level references
2.7 KiB
2.7 KiB
Patch-DB Patterns
Model and HasModel
Types stored in the database derive HasModel, which generates typed accessor methods on Model<T>:
#[derive(Debug, Deserialize, Serialize, HasModel)]
#[serde(rename_all = "camelCase")]
#[model = "Model<Self>"]
pub struct ServerInfo {
pub version: Version,
pub network: NetworkInfo,
// ...
}
Generated accessors (one per field):
as_version()—&Model<Version>as_version_mut()—&mut Model<Version>into_version()—Model<Version>
Model<T> APIs:
.de()— Deserialize toT.ser(&value)— Serialize fromT.mutate(|v| ...)— Deserialize, mutate, reserialize- For maps:
.keys(),.as_idx(&key),.insert(),.remove(),.contains_key()
Database Access
// Read-only snapshot
let snap = db.peek().await;
let version = snap.as_public().as_server_info().as_version().de()?;
// Atomic mutation
db.mutate(|db| {
db.as_public_mut().as_server_info_mut().as_version_mut().ser(&new_version)?;
Ok(())
}).await;
TypedDbWatch
Watch a JSON pointer path for changes and deserialize as a typed value. Requires T: HasModel.
Construction
use patch_db::json_ptr::JsonPointer;
let ptr: JsonPointer = "/public/serverInfo".parse().unwrap();
let mut watch = db.watch(ptr).await.typed::<ServerInfo>();
API
watch.peek()?.de()?— Get current value asTwatch.changed().await?— Wait until the watched path changeswatch.peek()?.as_field().de()?— Access nested fields viaHasModelaccessors
Usage Patterns
Wait for a condition, then proceed:
// Wait for DB version to match current OS version
let current = Current::default().semver();
let mut watch = db
.watch("/public/serverInfo".parse().unwrap())
.await
.typed::<ServerInfo>();
loop {
let server_info = watch.peek()?.de()?;
if server_info.version == current {
break;
}
watch.changed().await?;
}
React to changes in a loop:
// From net_controller.rs — react to host changes
let mut watch = db
.watch("/public/serverInfo/network/host".parse().unwrap())
.await
.typed::<Host>();
loop {
if let Err(e) = watch.changed().await {
tracing::error!("DB watch disconnected: {e}");
break;
}
let host = watch.peek()?.de()?;
// ... process host ...
}
Real Examples
net_controller.rs:469— WatchHostsfor package network changesnet_controller.rs:493— WatchHostfor main UI network changesservice_actor.rs:37— WatchStatusInfofor service state transitionsgateway.rs:1212— Wait for DB migrations to complete before syncing