webterm_agent/models/
frontend_registry.rs

1use crate::models::agent_error::AgentError;
2use crate::models::frontend::Frontend;
3use std::collections::HashMap;
4use std::sync::{Arc, OnceLock};
5use tokio::sync::{Mutex, RwLock};
6use tracing::debug;
7use webterm_core::types::FrontendId;
8
9// Frontend -> (has one) Session -> (has many) Activities
10// Frontends are transient and are lost when the agent switches a relay.
11// Sessions are persistent while the agent is running and a after switching to a new relay,
12// the frontend may reconnect to its existing session using a new frontend_id.
13pub struct FrontendRegistry {
14    map: RwLock<HashMap<FrontendId, Arc<Mutex<Frontend>>>>,
15}
16
17impl FrontendRegistry {
18    pub(crate) async fn singleton() -> &'static Self {
19        static INSTANCE: OnceLock<FrontendRegistry> = OnceLock::new();
20        INSTANCE.get_or_init(|| Self {
21            map: RwLock::new(HashMap::new()),
22        })
23    }
24
25    pub async fn find(frontend_id: FrontendId) -> Result<Arc<Mutex<Frontend>>, AgentError> {
26        let registry = Self::singleton().await;
27        registry
28            .map
29            .read()
30            .await
31            .get(&frontend_id)
32            .ok_or(AgentError::FrontendNotFound(Some(frontend_id)))
33            .cloned()
34    }
35
36    pub async fn build_frontend(
37        frontend_id: FrontendId,
38    ) -> Result<Arc<Mutex<Frontend>>, AgentError> {
39        let registry = Self::singleton().await;
40        debug!("Registering frontend {:?}", frontend_id);
41        let frontend = Arc::new(Mutex::new(Frontend::new(frontend_id)));
42        registry
43            .map
44            .write()
45            .await
46            .insert(frontend_id, frontend.clone());
47
48        Ok(frontend)
49    }
50}