use crate::types::EntityId;
use flume::{Receiver, Sender, unbounded};
use serde::Serialize;
use std::{
sync::{Arc, Mutex},
thread,
};
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
pub enum EntityEvent {
Created,
Updated,
Removed,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
pub enum AllEvent {
Reset,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
pub enum UndoRedoEvent {
Undone,
Redone,
BeginComposite,
EndComposite,
CancelComposite,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
pub enum LongOperationEvent {
Started,
Progress,
Cancelled,
Completed,
Failed,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
pub enum DirectAccessEntity {
All(AllEvent),
Root(EntityEvent),
Workspace(EntityEvent),
System(EntityEvent),
Entity(EntityEvent),
Field(EntityEvent),
Feature(EntityEvent),
File(EntityEvent),
UseCase(EntityEvent),
Dto(EntityEvent),
DtoField(EntityEvent),
Global(EntityEvent),
Relationship(EntityEvent),
UserInterface(EntityEvent),
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
pub enum HandlingAppLifecycleEvent {
InitializeApp,
CleanUpBeforeExit,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
pub enum HandlingManifestEvent {
Load,
Save,
Create,
Close,
ExportToMermaid,
Check,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
pub enum RustFileGenerationEvent {
FillRustFiles,
FillCodeInRustFiles,
GenerateRustCode,
GenerateRustFiles,
GenerateRustPrompt,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
pub enum CppQtFileGenerationEvent {
FillCppQtFiles,
GenerateCppQtCode,
GenerateCppQtFiles,
FillCodeInCppQtFiles,
GenerateCppQtPrompt,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
pub enum FileGenerationSharedStepsEvent {
FillStatusInFiles,
GetFileDiff,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
pub enum Origin {
DirectAccess(DirectAccessEntity),
UndoRedo(UndoRedoEvent),
LongOperation(LongOperationEvent),
HandlingAppLifecycle(HandlingAppLifecycleEvent),
HandlingManifest(HandlingManifestEvent),
RustFileGeneration(RustFileGenerationEvent),
CppQtFileGeneration(CppQtFileGenerationEvent),
FileGenerationSharedSteps(FileGenerationSharedStepsEvent),
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
pub struct Event {
pub origin: Origin,
pub ids: Vec<EntityId>,
pub data: Option<String>,
}
impl Event {
pub fn origin_string(&self) -> String {
match &self.origin {
Origin::DirectAccess(entity) => match entity {
DirectAccessEntity::All(event) => format!("direct_access_all_{:?}", event),
DirectAccessEntity::Root(event) => format!("direct_access_root_{:?}", event),
DirectAccessEntity::Workspace(event) => {
format!("direct_access_workspace_{:?}", event)
}
DirectAccessEntity::System(event) => format!("direct_access_system_{:?}", event),
DirectAccessEntity::Entity(event) => format!("direct_access_entity_{:?}", event),
DirectAccessEntity::Field(event) => format!("direct_access_field_{:?}", event),
DirectAccessEntity::Feature(event) => format!("direct_access_feature_{:?}", event),
DirectAccessEntity::File(event) => format!("direct_access_file_{:?}", event),
DirectAccessEntity::UseCase(event) => format!("direct_access_use_case_{:?}", event),
DirectAccessEntity::Dto(event) => format!("direct_access_dto_{:?}", event),
DirectAccessEntity::DtoField(event) => {
format!("direct_access_dto_field_{:?}", event)
}
DirectAccessEntity::Global(event) => format!("direct_access_global_{:?}", event),
DirectAccessEntity::Relationship(event) => {
format!("direct_access_relationship_{:?}", event)
}
DirectAccessEntity::UserInterface(event) => {
format!("direct_access_user_interface_{:?}", event)
}
},
Origin::UndoRedo(event) => format!("undo_redo_{:?}", event),
Origin::LongOperation(event) => format!("long_operation_{:?}", event),
Origin::HandlingAppLifecycle(event) => format!("handling_app_lifecycle_{:?}", event),
Origin::HandlingManifest(event) => format!("handling_manifest_{:?}", event),
Origin::RustFileGeneration(event) => format!("rust_file_generation_{:?}", event),
Origin::CppQtFileGeneration(event) => format!("cpp_qt_file_generation_{:?}", event),
Origin::FileGenerationSharedSteps(event) => {
format!("file_generation_shared_steps_{:?}", event)
}
}
.to_lowercase()
}
}
pub struct EventBuffer {
buffering: bool,
pending: Vec<Event>,
}
impl EventBuffer {
pub fn new() -> Self {
Self {
buffering: false,
pending: Vec::new(),
}
}
pub fn begin_buffering(&mut self) {
self.buffering = true;
self.pending.clear();
}
pub fn push(&mut self, event: Event) {
if self.buffering {
self.pending.push(event);
}
}
pub fn flush(&mut self) -> Vec<Event> {
self.buffering = false;
std::mem::take(&mut self.pending)
}
pub fn discard(&mut self) {
self.buffering = false;
self.pending.clear();
}
pub fn is_buffering(&self) -> bool {
self.buffering
}
}
impl Default for EventBuffer {
fn default() -> Self {
Self::new()
}
}
pub type Queue = Arc<Mutex<Vec<Event>>>;
pub struct EventHub {
sender: Sender<Event>,
receiver: Receiver<Event>,
queue: Queue,
}
impl Default for EventHub {
fn default() -> Self {
Self::new()
}
}
impl EventHub {
pub fn new() -> Self {
let (sender, receiver) = unbounded();
EventHub {
sender,
receiver,
queue: Arc::new(Mutex::new(Vec::new())),
}
}
pub fn start_event_loop(&self, shutdown_rx: Receiver<()>) -> thread::JoinHandle<()> {
let receiver = self.receiver.clone();
let queue = self.queue.clone();
thread::spawn(move || {
loop {
let outcome: Result<Option<Event>, ()> = flume::Selector::new()
.recv(&receiver, |r| r.map(Some).map_err(|_| ()))
.recv(&shutdown_rx, |_| Ok(None))
.wait();
match outcome {
Ok(Some(event)) => {
let mut queue = queue.lock().unwrap();
queue.push(event);
}
Ok(None) | Err(()) => break,
}
}
})
}
pub fn send_event(&self, event: Event) {
if let Err(e) = self.sender.send(event) {
eprintln!("EventHub: failed to send event (receiver dropped): {e}");
}
}
pub fn get_queue(&self) -> Queue {
self.queue.clone()
}
pub fn subscribe_receiver(&self) -> Receiver<Event> {
self.receiver.clone()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_event_hub_send_and_receive() {
let event_hub = EventHub::new();
let (shutdown_tx, shutdown_rx) = flume::bounded::<()>(1);
let handle = event_hub.start_event_loop(shutdown_rx);
let event = Event {
origin: Origin::DirectAccess(DirectAccessEntity::All(AllEvent::Reset)),
ids: vec![EntityId::default()],
data: Some("test_data".to_string()),
};
event_hub.send_event(event.clone());
thread::sleep(std::time::Duration::from_millis(100));
let queue = event_hub.get_queue();
let queue = queue.lock().unwrap();
assert_eq!(queue.len(), 1);
assert_eq!(queue[0], event);
drop(shutdown_tx);
handle.join().unwrap();
}
}