Got models and event system working

This commit is contained in:
Gregory Schier
2026-03-08 15:18:31 -07:00
parent 7382287bef
commit 0a616eb5e2
13 changed files with 181 additions and 52 deletions

View File

@@ -17,3 +17,4 @@ sea-query-rusqlite = { version = "0.7.0", features = ["with-chrono"] }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
thiserror = { workspace = true }
ts-rs = { workspace = true }

View File

@@ -12,7 +12,7 @@ pub use db_context::DbContext;
pub use error::{Error, Result};
pub use migrate::run_migrations;
pub use traits::{UpsertModelInfo, upsert_date};
pub use update_source::UpdateSource;
pub use update_source::{ModelChangeEvent, UpdateSource};
pub use util::{generate_id, generate_id_of_length, generate_prefixed_id};
// Re-export pool types that consumers will need

View File

@@ -1,4 +1,5 @@
use serde::{Deserialize, Serialize};
use ts_rs::TS;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case", tag = "type")]
@@ -15,3 +16,10 @@ impl UpdateSource {
Self::Window { label: label.into() }
}
}
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
#[serde(rename_all = "snake_case", tag = "type")]
pub enum ModelChangeEvent {
Upsert { created: bool },
Delete,
}

View File

@@ -1,5 +1,6 @@
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::mpsc;
/// Type-erased handler function: takes context + JSON payload, returns JSON or error.
type HandlerFn<Ctx> = Box<dyn Fn(&Ctx, serde_json::Value) -> Result<serde_json::Value, RpcError> + Send + Sync>;
@@ -101,29 +102,97 @@ impl<Ctx> RpcRouter<Ctx> {
}
}
/// Define RPC commands with a single source of truth.
/// A named event carrying a JSON payload, emitted from backend to frontend.
#[derive(Debug, Clone, Serialize)]
pub struct RpcEvent {
pub event: &'static str,
pub payload: serde_json::Value,
}
/// Channel-based event emitter. The backend calls `emit()`, the transport
/// adapter (Tauri, WebSocket, etc.) drains the receiver and delivers events.
#[derive(Clone)]
pub struct RpcEventEmitter {
tx: mpsc::Sender<RpcEvent>,
}
impl RpcEventEmitter {
pub fn new() -> (Self, mpsc::Receiver<RpcEvent>) {
let (tx, rx) = mpsc::channel();
(Self { tx }, rx)
}
/// Emit a typed event. Serializes the payload to JSON.
pub fn emit<T: Serialize>(&self, event: &'static str, payload: &T) {
if let Ok(value) = serde_json::to_value(payload) {
let _ = self.tx.send(RpcEvent { event, payload: value });
}
}
}
/// Define RPC commands and events with a single source of truth.
///
/// Generates:
/// - `build_router()` — creates an `RpcRouter` with all handlers registered
/// - `RpcSchema` — a struct with ts-rs derives for TypeScript type generation
/// - `RpcEventSchema` — (if events declared) a struct mapping event names to payload types
///
/// The wire name for each command/event is derived from `stringify!($ident)`.
///
/// # Example
/// ```ignore
/// define_rpc! {
/// ProxyCtx;
/// "proxy_start" => proxy_start(ProxyStartRequest) -> ProxyStartResponse,
/// "proxy_stop" => proxy_stop(ProxyStopRequest) -> bool,
/// commands {
/// proxy_start(ProxyStartRequest) -> ProxyStartResponse,
/// proxy_stop(ProxyStopRequest) -> bool,
/// }
/// events {
/// model_write(ModelPayload),
/// }
/// }
/// ```
#[macro_export]
macro_rules! define_rpc {
// With both commands and events
(
$ctx:ty;
$( $name:literal => $handler:ident ( $req:ty ) -> $res:ty ),* $(,)?
commands {
$( $handler:ident ( $req:ty ) -> $res:ty ),* $(,)?
}
events {
$( $evt_ident:ident ( $evt_payload:ty ) ),* $(,)?
}
) => {
pub fn build_router() -> $crate::RpcRouter<$ctx> {
let mut router = $crate::RpcRouter::new();
$( router.register($name, $crate::rpc_handler!($handler)); )*
$( router.register(stringify!($handler), $crate::rpc_handler!($handler)); )*
router
}
#[derive(ts_rs::TS)]
#[ts(export, export_to = "gen_rpc.ts")]
pub struct RpcSchema {
$( pub $handler: ($req, $res), )*
}
#[derive(ts_rs::TS)]
#[ts(export, export_to = "gen_rpc.ts")]
pub struct RpcEventSchema {
$( pub $evt_ident: $evt_payload, )*
}
};
// Commands only (no events)
(
$ctx:ty;
commands {
$( $handler:ident ( $req:ty ) -> $res:ty ),* $(,)?
}
) => {
pub fn build_router() -> $crate::RpcRouter<$ctx> {
let mut router = $crate::RpcRouter::new();
$( router.register(stringify!($handler), $crate::rpc_handler!($handler)); )*
router
}

View File

@@ -10,7 +10,7 @@ use std::collections::BTreeMap;
use ts_rs::TS;
use yaak_core::WorkspaceContext;
pub use yaak_database::{generate_id, generate_id_of_length, generate_prefixed_id};
pub use yaak_database::{ModelChangeEvent, generate_id, generate_id_of_length, generate_prefixed_id};
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
#[serde(rename_all = "camelCase")]
@@ -21,14 +21,6 @@ pub struct ModelPayload {
pub change: ModelChangeEvent,
}
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
#[serde(rename_all = "snake_case", tag = "type")]
#[ts(export, export_to = "gen_models.ts")]
pub enum ModelChangeEvent {
Upsert { created: bool },
Delete,
}
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
#[serde(rename_all = "snake_case", tag = "type")]
#[ts(export, export_to = "gen_models.ts")]