text-document-frontend 1.6.3

Frontend integration layer and command wrappers for text-document
Documentation
// Generated by Qleany v1.7.3 from frontend_event_hub_client.tera
use common::event::{Event, EventHub, Origin};
use flume::{Receiver, Selector};
use parking_lot::Mutex;
use std::collections::HashMap;
use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
use std::thread;

/// Event callback type
pub type EventCallback = Box<dyn Fn(Event) + Send>;

/// Internal storage: each subscriber entry pairs its id (for O(n) removal on
/// token drop) with its callback. Vec preserves registration order so
/// subscribers fire deterministically.
type SubscriberList = Vec<(u64, EventCallback)>;

/// EventHubClient - handles event passing from backend to UI
/// Subscribe callbacks to specific event origins and start the event loop.
#[derive(Clone)]
pub struct EventHubClient {
    subscribers: Arc<Mutex<HashMap<Origin, SubscriberList>>>,
    receiver: Receiver<Event>,
    next_subscriber_id: Arc<AtomicU64>,
}

impl EventHubClient {
    /// Create a new event hub client
    pub fn new(event_hub: &EventHub) -> Self {
        EventHubClient {
            subscribers: Arc::new(Mutex::new(HashMap::new())),
            receiver: event_hub.subscribe_receiver(),
            next_subscriber_id: Arc::new(AtomicU64::new(0)),
        }
    }

    /// Subscribe a callback to an origin. The returned `SubscriptionToken`
    /// removes the callback from the subscribers map when dropped — hold it
    /// for as long as you want the callback to fire, then drop it to
    /// unsubscribe.
    pub fn subscribe<F>(&self, origin: Origin, callback: F) -> SubscriptionToken
    where
        F: Fn(Event) + Send + 'static,
    {
        let id = self.next_subscriber_id.fetch_add(1, Ordering::Relaxed);
        {
            let mut subs = self.subscribers.lock();
            subs.entry(origin.clone())
                .or_default()
                .push((id, Box::new(callback)));
        }
        SubscriptionToken {
            subscribers: Arc::clone(&self.subscribers),
            origin,
            id,
        }
    }

    /// Start the event loop in a background thread.
    ///
    /// The thread blocks on a `flume::Selector` that waits on either an
    /// incoming event or the shutdown receiver. Zero CPU while idle —
    /// no polling, no timeout. The thread exits when the matching
    /// shutdown `Sender` is dropped (which makes `shutdown_rx`
    /// `Disconnected`) or when the event hub's sender drops.
    pub fn start(&self, shutdown_rx: Receiver<()>) {
        let receiver = self.receiver.clone();
        let subscribers = Arc::clone(&self.subscribers);

        log::info!("EventHubClient starting event loop");

        thread::spawn(move || {
            log::info!("EventHubClient event loop started");
            loop {
                // True blocking wait. Both branches reduce to
                // `Result<Option<Event>, ()>`: `Ok(Some(event))` for a
                // real delivery, `Ok(None)` for shutdown (either the
                // shutdown sender was dropped or a `()` was actually
                // sent), `Err(())` for event-channel disconnect.
                let outcome: Result<Option<Event>, ()> = Selector::new()
                    .recv(&receiver, |r| r.map(Some).map_err(|_| ()))
                    .recv(&shutdown_rx, |_| Ok(None))
                    .wait();
                match outcome {
                    Ok(Some(event)) => {
                        log::debug!("EventHubClient received event: {:?}", event);
                        let subs = subscribers.lock();
                        if let Some(callbacks) = subs.get(&event.origin) {
                            for (_id, callback) in callbacks {
                                callback(event.clone());
                            }
                        }
                    }
                    Ok(None) => {
                        log::info!("EventHubClient quitting event loop");
                        break;
                    }
                    Err(()) => {
                        log::info!("EventHubClient channel disconnected");
                        break;
                    }
                }
            }
        });
    }
}

/// Opaque handle returned by `EventHubClient::subscribe`. Dropping it removes
/// the associated callback from the subscribers map; the origin's entry is
/// removed entirely when its last subscriber goes away.
pub struct SubscriptionToken {
    subscribers: Arc<Mutex<HashMap<Origin, SubscriberList>>>,
    origin: Origin,
    id: u64,
}

impl Drop for SubscriptionToken {
    fn drop(&mut self) {
        // Best-effort removal. parking_lot mutexes don't poison, so the
        // lock always succeeds (no double-panic risk during unwind).
        let mut subs = self.subscribers.lock();
        if let Some(list) = subs.get_mut(&self.origin) {
            list.retain(|(id, _)| *id != self.id);
            if list.is_empty() {
                subs.remove(&self.origin);
            }
        }
    }
}