use std::sync::Arc; use dashmap::DashMap; use tera::Tera; use tokio::sync::broadcast; use uuid::Uuid; use common_contracts::observability::TaskProgress; use crate::{config::AppConfig, templates::load_tera}; #[derive(Clone)] pub struct AppState { pub tasks: Arc>, pub streams: Arc, pub config: Arc, #[allow(dead_code)] pub tera: Arc, pub nats: async_nats::Client, } pub struct StreamManager { // Key: RequestId (Uuid) // Value: Broadcast Sender. We send JSON strings. pub channels: DashMap>, } impl StreamManager { pub fn new() -> Self { Self { channels: DashMap::new(), } } pub fn get_or_create_sender(&self, request_id: Uuid) -> broadcast::Sender { self.channels.entry(request_id).or_insert_with(|| { // Buffer size 100 messages. If lag occurs, receiver might miss messages (RecvError::Lagged). // Since we use this for real-time text streaming, missing a chunk is bad but acceptable for "pseudo" visual if we can recover, // but here we don't want to miss content. // Frontend SSE usually just reconnects. // We should probably use a larger buffer to be safe, e.g. 1000. let (tx, _rx) = broadcast::channel(1000); tx }).value().clone() } #[allow(dead_code)] pub fn remove_channel(&self, request_id: &Uuid) { self.channels.remove(request_id); } } impl AppState { pub fn new(config: AppConfig, nats: async_nats::Client) -> Self { Self { tasks: Arc::new(DashMap::new()), streams: Arc::new(StreamManager::new()), config: Arc::new(config), tera: Arc::new(load_tera()), nats, } } }