engram/realtime/
events.rs1use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5
6use crate::types::MemoryId;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
10#[serde(rename_all = "snake_case")]
11pub enum EventType {
12 MemoryCreated,
13 MemoryUpdated,
14 MemoryDeleted,
15 CrossrefCreated,
16 CrossrefDeleted,
17 SyncStarted,
18 SyncCompleted,
19 SyncFailed,
20}
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct RealtimeEvent {
25 #[serde(rename = "type")]
27 pub event_type: EventType,
28 pub timestamp: DateTime<Utc>,
30 pub memory_id: Option<MemoryId>,
32 pub preview: Option<String>,
34 pub changes: Option<Vec<String>>,
36 pub data: Option<serde_json::Value>,
38}
39
40impl RealtimeEvent {
41 pub fn memory_created(id: MemoryId, preview: String) -> Self {
43 Self {
44 event_type: EventType::MemoryCreated,
45 timestamp: Utc::now(),
46 memory_id: Some(id),
47 preview: Some(truncate(&preview, 100)),
48 changes: None,
49 data: None,
50 }
51 }
52
53 pub fn memory_updated(id: MemoryId, changes: Vec<String>) -> Self {
55 Self {
56 event_type: EventType::MemoryUpdated,
57 timestamp: Utc::now(),
58 memory_id: Some(id),
59 preview: None,
60 changes: Some(changes),
61 data: None,
62 }
63 }
64
65 pub fn memory_deleted(id: MemoryId) -> Self {
67 Self {
68 event_type: EventType::MemoryDeleted,
69 timestamp: Utc::now(),
70 memory_id: Some(id),
71 preview: None,
72 changes: None,
73 data: None,
74 }
75 }
76
77 pub fn sync_completed(direction: &str, changes: i64) -> Self {
79 Self {
80 event_type: EventType::SyncCompleted,
81 timestamp: Utc::now(),
82 memory_id: None,
83 preview: None,
84 changes: None,
85 data: Some(serde_json::json!({
86 "direction": direction,
87 "changes": changes,
88 })),
89 }
90 }
91
92 pub fn sync_failed(error: &str) -> Self {
94 Self {
95 event_type: EventType::SyncFailed,
96 timestamp: Utc::now(),
97 memory_id: None,
98 preview: None,
99 changes: None,
100 data: Some(serde_json::json!({
101 "error": error,
102 })),
103 }
104 }
105}
106
107fn truncate(s: &str, max: usize) -> String {
109 if s.chars().count() <= max {
110 s.to_string()
111 } else {
112 let truncated: String = s.chars().take(max.saturating_sub(3)).collect();
114 format!("{}...", truncated)
115 }
116}
117
118#[derive(Debug, Clone, Default, Serialize, Deserialize)]
120pub struct SubscriptionFilter {
121 pub memory_ids: Option<Vec<MemoryId>>,
123 pub tags: Option<Vec<String>>,
125 pub event_types: Option<Vec<EventType>>,
127}
128
129impl SubscriptionFilter {
130 pub fn matches(&self, event: &RealtimeEvent) -> bool {
132 if let Some(ref types) = self.event_types {
134 if !types.contains(&event.event_type) {
135 return false;
136 }
137 }
138
139 if let Some(ref ids) = self.memory_ids {
141 if let Some(event_id) = event.memory_id {
142 if !ids.contains(&event_id) {
143 return false;
144 }
145 }
146 }
147
148 true
152 }
153}