feltdb/
materialization.rs1use crate::{AcquisitionMetadata, FlowRef, Operation};
9use serde_json::Value;
10use std::time::SystemTime;
11use std::time::UNIX_EPOCH;
12
13#[derive(Clone)]
15pub struct MaterializationHandler {
16 materialized: std::sync::Arc<std::sync::Mutex<std::collections::HashSet<String>>>,
18}
19
20impl MaterializationHandler {
21 pub fn new() -> Self {
23 Self {
24 materialized: std::sync::Arc::new(std::sync::Mutex::new(
25 std::collections::HashSet::new(),
26 )),
27 }
28 }
29
30 pub fn materialize_as_operation(
33 &self,
34 flow_ref: &FlowRef,
35 content: Vec<u8>,
36 metadata: &AcquisitionMetadata,
37 ) -> Result<Operation, String> {
38 let value: Value = serde_json::from_slice(&content).unwrap_or_else(|_| {
40 Value::String(String::from_utf8_lossy(&content).into_owned())
42 });
43
44 let key = format!("{}:{}", flow_ref.namespace, flow_ref.object_id);
45 let now_ms = SystemTime::now()
46 .duration_since(UNIX_EPOCH)
47 .map(|d| d.as_millis())
48 .unwrap_or(0);
49
50 let mut op = Operation::update(
53 now_ms as u64, metadata.source_peer.id.clone(), 1, key.clone(),
57 value,
58 flow_ref.namespace.clone(),
59 flow_ref.namespace.clone(),
60 );
61
62 if let Some(vector_clock) = &metadata.vector_clock {
64 op = op.with_vector_clock(vector_clock.clone());
65 }
66
67 let mut materialized = self.materialized.lock().map_err(|e| e.to_string())?;
69 materialized.insert(key);
70
71 Ok(op)
72 }
73
74 pub fn is_materialized(&self, flow_ref: &FlowRef) -> Result<bool, String> {
76 let key = format!("{}:{}", flow_ref.namespace, flow_ref.object_id);
77 let materialized = self.materialized.lock().map_err(|e| e.to_string())?;
78 Ok(materialized.contains(&key))
79 }
80
81 pub fn materialized_count(&self) -> Result<usize, String> {
83 let materialized = self.materialized.lock().map_err(|e| e.to_string())?;
84 Ok(materialized.len())
85 }
86}
87
88impl Default for MaterializationHandler {
89 fn default() -> Self {
90 Self::new()
91 }
92}
93
94pub trait ReactiveCollectionIntegration {
97 fn on_content_materialized(&self, operation: &Operation) -> Result<(), String>;
99
100 fn affects_collection(&self, namespace: &str) -> bool;
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107 use crate::PeerId;
108
109 #[test]
110 fn test_materialization_handler_creation() {
111 let handler = MaterializationHandler::new();
112 assert_eq!(handler.materialized_count().unwrap(), 0);
113 }
114
115 #[test]
116 fn test_materialize_as_operation() {
117 let handler = MaterializationHandler::new();
118 let flow_ref = FlowRef::new("docs", "doc-123");
119 let content = br#"{"title": "Test Document", "content": "Test content"}"#.to_vec();
120 let peer = PeerId::new("peer-a");
121 let metadata = AcquisitionMetadata::new(peer, true);
122
123 let op = handler
124 .materialize_as_operation(&flow_ref, content, &metadata)
125 .unwrap();
126
127 assert_eq!(op.key, "docs:doc-123");
128 assert_eq!(op.capability, "docs");
129 assert!(op.value.is_some());
130 }
131
132 #[test]
133 fn test_materialize_non_json_content() {
134 let handler = MaterializationHandler::new();
135 let flow_ref = FlowRef::new("binary", "file-456");
136 let content = b"raw binary data".to_vec();
137 let peer = PeerId::new("peer-b");
138 let metadata = AcquisitionMetadata::new(peer, true);
139
140 let op = handler
141 .materialize_as_operation(&flow_ref, content, &metadata)
142 .unwrap();
143
144 assert_eq!(op.key, "binary:file-456");
145 assert!(op.value.is_some());
147 }
148
149 #[test]
150 fn test_is_materialized() {
151 let handler = MaterializationHandler::new();
152 let flow_ref = FlowRef::new("docs", "doc-789");
153
154 assert!(!handler.is_materialized(&flow_ref).unwrap());
155
156 let content = b"content".to_vec();
157 let peer = PeerId::new("peer-c");
158 let metadata = AcquisitionMetadata::new(peer, true);
159
160 handler
161 .materialize_as_operation(&flow_ref, content, &metadata)
162 .unwrap();
163
164 assert!(handler.is_materialized(&flow_ref).unwrap());
165 }
166
167 #[test]
168 fn test_materialized_count() {
169 let handler = MaterializationHandler::new();
170
171 for i in 0..5 {
172 let flow_ref = FlowRef::new("docs", &format!("doc-{}", i));
173 let content = b"content".to_vec();
174 let peer = PeerId::new("peer-x");
175 let metadata = AcquisitionMetadata::new(peer, true);
176
177 handler
178 .materialize_as_operation(&flow_ref, content, &metadata)
179 .unwrap();
180 }
181
182 assert_eq!(handler.materialized_count().unwrap(), 5);
183 }
184}