use crate::types::EntityId;
use flume::{Receiver, Sender, unbounded};
use serde::Serialize;
use std::{
sync::{Arc, Mutex, atomic::AtomicBool},
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),
Document(EntityEvent),
Frame(EntityEvent),
Block(EntityEvent),
InlineElement(EntityEvent),
List(EntityEvent),
Resource(EntityEvent),
Table(EntityEvent),
TableCell(EntityEvent),
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
pub enum DocumentEditingEvent {
InsertText,
DeleteText,
InsertBlock,
InsertImage,
InsertFrame,
InsertFormattedText,
CreateList,
InsertList,
InsertFragment,
InsertHtmlAtPosition,
InsertMarkdownAtPosition,
InsertTable,
RemoveTable,
InsertTableRow,
InsertTableColumn,
RemoveTableRow,
RemoveTableColumn,
MergeTableCells,
SplitTableCell,
AddBlockToList,
RemoveBlockFromList,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
pub enum DocumentFormattingEvent {
SetTextFormat,
MergeTextFormat,
SetBlockFormat,
SetFrameFormat,
SetTableFormat,
SetTableCellFormat,
SetListFormat,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
pub enum DocumentIoEvent {
ImportPlainText,
ExportPlainText,
ImportMarkdown,
ExportMarkdown,
ImportHtml,
ExportHtml,
ExportLatex,
ExportDocx,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
pub enum DocumentSearchEvent {
FindText,
FindAll,
ReplaceText,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
pub enum DocumentInspectionEvent {
GetDocumentStats,
GetTextAtPosition,
GetBlockAtPosition,
ExtractFragment,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize)]
pub enum Origin {
DirectAccess(DirectAccessEntity),
UndoRedo(UndoRedoEvent),
LongOperation(LongOperationEvent),
DocumentEditing(DocumentEditingEvent),
DocumentFormatting(DocumentFormattingEvent),
DocumentIo(DocumentIoEvent),
DocumentSearch(DocumentSearchEvent),
DocumentInspection(DocumentInspectionEvent),
}
#[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::Document(event) => {
format!("direct_access_document_{:?}", event)
}
DirectAccessEntity::Frame(event) => format!("direct_access_frame_{:?}", event),
DirectAccessEntity::Block(event) => format!("direct_access_block_{:?}", event),
DirectAccessEntity::InlineElement(event) => {
format!("direct_access_inline_element_{:?}", event)
}
DirectAccessEntity::List(event) => format!("direct_access_list_{:?}", event),
DirectAccessEntity::Resource(event) => {
format!("direct_access_resource_{:?}", event)
}
DirectAccessEntity::Table(event) => format!("direct_access_table_{:?}", event),
DirectAccessEntity::TableCell(event) => {
format!("direct_access_table_cell_{:?}", event)
}
},
Origin::UndoRedo(event) => format!("undo_redo_{:?}", event),
Origin::LongOperation(event) => format!("long_operation_{:?}", event),
Origin::DocumentEditing(event) => format!("document_editing_{:?}", event),
Origin::DocumentFormatting(event) => format!("document_formatting_{:?}", event),
Origin::DocumentIo(event) => format!("document_io_{:?}", event),
Origin::DocumentSearch(event) => format!("document_search_{:?}", event),
Origin::DocumentInspection(event) => format!("document_inspection_{:?}", 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, stop_signal: Arc<AtomicBool>) {
let receiver = self.receiver.clone();
let queue = self.queue.clone();
thread::spawn(move || {
loop {
if stop_signal.load(std::sync::atomic::Ordering::Relaxed) {
break;
}
match receiver.recv() {
Ok(event) => {
let mut queue = queue.lock().unwrap();
queue.push(event.clone());
}
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 stop_signal = Arc::new(AtomicBool::new(false));
event_hub.start_event_loop(stop_signal.clone());
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);
stop_signal.store(true, std::sync::atomic::Ordering::Relaxed);
}
}