Adding some IPC.

This commit is contained in:
Micheal Smith
2025-11-09 00:02:38 -06:00
parent 7f7981d6cd
commit 5d390ee9f3
11 changed files with 174 additions and 64 deletions

89
src/event_manager.rs Normal file
View File

@@ -0,0 +1,89 @@
use std::{collections::VecDeque, path::Path, sync::Arc};
use color_eyre::Result;
use tokio::{
io::AsyncWriteExt,
net::{UnixListener, UnixStream},
sync::{RwLock, broadcast},
};
use tracing::error;
use crate::event::Event;
// Hard coding for now. Maybe make this a parameter to new.
const EVENT_BUF_MAX: usize = 1000;
// Manager for communication with plugins.
pub struct EventManager {
announce: broadcast::Sender<String>, // Everything broadcasts here.
events: Arc<RwLock<VecDeque<String>>>, // Ring buffer.
}
impl EventManager {
pub fn new() -> Result<Self> {
let (announce, _) = broadcast::channel(100);
Ok(Self {
announce,
events: Arc::new(RwLock::new(VecDeque::<String>::new())),
})
}
pub async fn broadcast(&self, event: &Event) -> Result<()> {
let msg = serde_json::to_string(event)? + "\n";
let mut events = self.events.write().await;
if events.len() >= EVENT_BUF_MAX {
events.pop_front();
}
events.push_back(msg.clone());
drop(events);
let _ = self.announce.send(msg);
Ok(())
}
pub async fn start_listening(self: Arc<Self>, path: impl AsRef<Path>) {
let listener = UnixListener::bind(path).unwrap();
loop {
match listener.accept().await {
Ok((stream, _)) => {
// Spawn a new stream for the plugin. The loop
// runs recursively from there.
let broadcaster = Arc::clone(&self);
tokio::spawn(async move {
// send events.
let _ = broadcaster.send_events(stream).await;
});
}
Err(e) => error!("Accept error: {e}"),
}
}
}
async fn send_events(&self, stream: UnixStream) -> Result<()> {
let mut writer = stream;
// Take care of history.
let events = self.events.read().await;
for event in events.iter() {
writer.write_all(event.as_bytes()).await?;
}
drop(events);
// Now just broadcast the new events.
let mut rx = self.announce.subscribe();
while let Ok(event) = rx.recv().await {
if writer.write_all(event.as_bytes()).await.is_err() {
// *click*
break;
}
}
Ok(())
}
}