evidentsource_core/domain/
events.rs

1//! Event types for the event log.
2//!
3//! This module defines the core event types used in the database.
4//!
5//! EvidentSource uses CloudEvents as its event format, but with specialized semantics:
6//! - `ProspectiveEvent`: Events before storage, where `stream` is the simple stream name
7//! - `Event`: Events after storage, where `source` is a full URI containing the stream path
8
9use chrono::{DateTime, Utc};
10
11/// Event data payload.
12#[derive(Debug, Clone, PartialEq)]
13pub enum EventData {
14    /// Binary data payload.
15    Binary(Vec<u8>),
16    /// String data payload (typically JSON).
17    String(String),
18}
19
20impl EventData {
21    /// Get the data as bytes.
22    pub fn as_bytes(&self) -> &[u8] {
23        match self {
24            EventData::Binary(bytes) => bytes,
25            EventData::String(s) => s.as_bytes(),
26        }
27    }
28}
29
30/// Extension attribute value types supported by CloudEvents.
31#[derive(Debug, Clone, PartialEq)]
32pub enum ExtensionValue {
33    /// Boolean value.
34    Boolean(bool),
35    /// Integer value (CloudEvents spec: -2,147,483,648 to +2,147,483,647).
36    Integer(i64),
37    /// String value.
38    String(String),
39}
40
41/// A prospective event before storage.
42///
43/// In EvidentSource, prospective events use the stream name directly as the source.
44/// When stored, the server transforms this into a full URI in the `Event` type.
45#[derive(Debug, Clone)]
46pub struct ProspectiveEvent {
47    /// Unique identifier for this event (typically UUID).
48    pub id: String,
49    /// The stream this event belongs to. Maps to CloudEvents `source`.
50    pub stream: String,
51    /// The event type (e.g., "com.example.account.opened").
52    pub event_type: String,
53    /// Optional subject identifying the entity this event is about.
54    pub subject: Option<String>,
55    /// Optional event data payload.
56    pub data: Option<EventData>,
57    /// Optional timestamp when the event occurred.
58    pub time: Option<DateTime<Utc>>,
59    /// Optional content type of the data (e.g., "application/json").
60    pub datacontenttype: Option<String>,
61    /// Optional schema URI for the data.
62    pub dataschema: Option<String>,
63    /// Extension attributes.
64    pub extensions: Vec<(String, ExtensionValue)>,
65}
66
67impl ProspectiveEvent {
68    /// Get the stream name.
69    pub fn stream(&self) -> &str {
70        &self.stream
71    }
72
73    /// Get the event type.
74    pub fn event_type(&self) -> &str {
75        &self.event_type
76    }
77
78    /// Get the subject, if present.
79    pub fn subject(&self) -> Option<&str> {
80        self.subject.as_deref()
81    }
82
83    /// Get the data payload, if present.
84    pub fn data(&self) -> Option<&EventData> {
85        self.data.as_ref()
86    }
87
88    /// Get the timestamp, if present.
89    pub fn time(&self) -> Option<DateTime<Utc>> {
90        self.time
91    }
92
93    /// Get an extension value by key.
94    pub fn extension(&self, key: &str) -> Option<&ExtensionValue> {
95        self.extensions
96            .iter()
97            .find(|(k, _)| k == key)
98            .map(|(_, v)| v)
99    }
100}
101
102/// A stored event with a full source URI.
103///
104/// After storage, the EvidentSource server transforms the stream name into a full URI
105/// like `https://host/db/database-name/streams/stream-name`.
106#[derive(Debug, Clone)]
107pub struct Event {
108    /// Unique identifier for this event.
109    pub id: String,
110    /// Full source URI (e.g., "https://host/db/name/streams/stream").
111    pub source: String,
112    /// The event type (e.g., "com.example.account.opened").
113    pub event_type: String,
114    /// Optional subject identifying the entity this event is about.
115    pub subject: Option<String>,
116    /// Optional event data payload.
117    pub data: Option<EventData>,
118    /// Optional timestamp when the event occurred.
119    pub time: Option<DateTime<Utc>>,
120    /// Optional content type of the data.
121    pub datacontenttype: Option<String>,
122    /// Optional schema URI for the data.
123    pub dataschema: Option<String>,
124    /// Extension attributes.
125    pub extensions: Vec<(String, ExtensionValue)>,
126}
127
128impl Event {
129    /// Get the full source URI.
130    pub fn source(&self) -> &str {
131        &self.source
132    }
133
134    /// Extract the stream name from the source URI.
135    ///
136    /// Parses URIs like `https://host/db/name/streams/my-stream` to extract `my-stream`.
137    /// Returns `None` if the URI doesn't match the expected pattern.
138    pub fn stream(&self) -> Option<&str> {
139        // Look for /streams/ in the path and return everything after it
140        self.source
141            .find("/streams/")
142            .map(|idx| &self.source[idx + 9..]) // 9 = len("/streams/")
143    }
144
145    /// Get the event type.
146    pub fn event_type(&self) -> &str {
147        &self.event_type
148    }
149
150    /// Get the subject, if present.
151    pub fn subject(&self) -> Option<&str> {
152        self.subject.as_deref()
153    }
154
155    /// Get the data payload, if present.
156    pub fn data(&self) -> Option<&EventData> {
157        self.data.as_ref()
158    }
159
160    /// Get the timestamp, if present.
161    pub fn time(&self) -> Option<DateTime<Utc>> {
162        self.time
163    }
164
165    /// Get an extension value by key.
166    pub fn extension(&self, key: &str) -> Option<&ExtensionValue> {
167        self.extensions
168            .iter()
169            .find(|(k, _)| k == key)
170            .map(|(_, v)| v)
171    }
172}
173
174/// A transaction of events that were committed together.
175#[derive(Debug, Clone)]
176pub struct Transaction {
177    /// The events in this transaction.
178    pub events: Vec<Event>,
179    /// Summary information about the transaction.
180    pub summary: TransactionSummary,
181}
182
183/// Summary information about a committed transaction.
184#[derive(Debug, Clone)]
185pub struct TransactionSummary {
186    /// Optional transaction identifier.
187    pub transaction_id: Option<String>,
188    /// The revision at which this transaction was committed.
189    pub revision: u64,
190    /// Number of events in the transaction.
191    pub event_count: usize,
192}