# Patch-DB Patterns ## Model and HasModel Types stored in the database derive `HasModel`, which generates typed accessor methods on `Model`: ```rust #[derive(Debug, Deserialize, Serialize, HasModel)] #[serde(rename_all = "camelCase")] #[model = "Model"] pub struct ServerInfo { pub version: Version, pub network: NetworkInfo, // ... } ``` **Generated accessors** (one per field): - `as_version()` — `&Model` - `as_version_mut()` — `&mut Model` - `into_version()` — `Model` **`Model` APIs:** - `.de()` — Deserialize to `T` - `.ser(&value)` — Serialize from `T` - `.mutate(|v| ...)` — Deserialize, mutate, reserialize - For maps: `.keys()`, `.as_idx(&key)`, `.insert()`, `.remove()`, `.contains_key()` ## Database Access ```rust // 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 ```rust use patch_db::json_ptr::JsonPointer; let ptr: JsonPointer = "/public/serverInfo".parse().unwrap(); let mut watch = db.watch(ptr).await.typed::(); ``` ### API - `watch.peek()?.de()?` — Get current value as `T` - `watch.changed().await?` — Wait until the watched path changes - `watch.peek()?.as_field().de()?` — Access nested fields via `HasModel` accessors ### Usage Patterns **Wait for a condition, then proceed:** ```rust // 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::(); loop { let server_info = watch.peek()?.de()?; if server_info.version == current { break; } watch.changed().await?; } ``` **React to changes in a loop:** ```rust // From net_controller.rs — react to host changes let mut watch = db .watch("/public/serverInfo/network/host".parse().unwrap()) .await .typed::(); 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` — Watch `Hosts` for package network changes - `net_controller.rs:493` — Watch `Host` for main UI network changes - `service_actor.rs:37` — Watch `StatusInfo` for service state transitions - `gateway.rs:1212` — Wait for DB migrations to complete before syncing