bodged refactor

This commit is contained in:
2026-03-08 20:10:15 +01:00
parent 71e63e4410
commit b10326b242
12 changed files with 801 additions and 339 deletions
+59 -11
View File
@@ -3,8 +3,10 @@ mod queries;
mod todo_list;
mod about;
use livevue_rs::{AppState, ConnectionManager, SubscriptionRegistry};
use livevue_rs::{AppState, CacheKey, RenderContext, Store, spawn_fanout};
use crate::app::{app_wrapper, resolve_and_render};
use std::sync::Arc;
use std::time::Duration;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
@@ -12,10 +14,8 @@ async fn main() -> anyhow::Result<()> {
// Database setup
// -----------------------------------------------------------------------
// SQLite with create-if-missing. In-memory `:memory:` also works for dev.
let db = sqlx::SqlitePool::connect("sqlite:todo.db?mode=rwc").await?;
// Run migrations inline (no migration files needed for v0).
sqlx::query(
"CREATE TABLE IF NOT EXISTS todos (
id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -26,7 +26,6 @@ async fn main() -> anyhow::Result<()> {
.execute(&db)
.await?;
// Seed a few todos if the table is empty (nice for first run)
let count: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM todos")
.fetch_one(&db)
.await?;
@@ -39,14 +38,65 @@ async fn main() -> anyhow::Result<()> {
}
}
// -----------------------------------------------------------------------
// Store
// -----------------------------------------------------------------------
let store = Store::new(db);
// -----------------------------------------------------------------------
// Render function
// -----------------------------------------------------------------------
// The render_fn is the app's half of the framework contract. It receives
// a RenderContext (with signals, tree, store access) and returns the
// rendered inner HTML plus the accumulated subscription keys.
//
// The fanout task calls this for every server-initiated rerender.
let render_fn: livevue_rs::RenderFn = Arc::new(|mut cx: RenderContext| {
Box::pin(async move {
println!("Render Called");
let signals_json = cx.signals.to_json_string();
let conn_id = cx.connection_id;
let html = resolve_and_render(&mut cx).await?;
let keys = cx.take_subscription_keys();
// Wrap in app_wrapper so DataStar morphs the full #app element.
// Signals are re-emitted from the stored json so the client stays
// in sync even on server-initiated rerenders.
let wrapped = app_wrapper(conn_id, &signals_json, &html);
Ok((wrapped, keys))
})
});
// -----------------------------------------------------------------------
// Application state
// -----------------------------------------------------------------------
let state = Arc::new(AppState {
db,
connections: ConnectionManager::new(),
subscriptions: SubscriptionRegistry::new(),
let state = AppState::new(store.clone(), render_fn);
// -----------------------------------------------------------------------
// Fanout task
// -----------------------------------------------------------------------
spawn_fanout(state.clone());
// -----------------------------------------------------------------------
// Clock ticker
//
// Publishes CacheKey::Channel { key: "clock" } every second. Any
// connection whose last render called cx.kv_run("clock") will be
// scheduled for a rerender by the fanout task.
// -----------------------------------------------------------------------
let clock_store = store.clone();
tokio::spawn(async move {
let mut interval = tokio::time::interval(Duration::from_secs(1));
loop {
interval.tick().await;
let now = chrono::Utc::now().format("%H:%M:%S UTC").to_string();
// set() writes the value and publishes the invalidation event.
clock_store.kv.set("clock", serde_json::json!(now));
}
});
// -----------------------------------------------------------------------
@@ -54,11 +104,9 @@ async fn main() -> anyhow::Result<()> {
// -----------------------------------------------------------------------
let app = app::router(state);
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
println!("Todo example listening on http://localhost:3000");
axum::serve(listener, app).await?;
Ok(())
}
}