surreal_sync_runtime/pipeline/apply/
transform.rs1use crate::pipeline::apply::ApplyEvent;
4use crate::pipeline::pipeline::Pipeline;
5use anyhow::{bail, Result};
6use async_trait::async_trait;
7use surreal_sync_core::{Change, Relation, RelationChange, Row};
8
9#[async_trait]
32pub trait BatchTransformer: Send + Sync {
33 fn is_identity(&self) -> bool;
38
39 async fn transform_changes(&self, batch_id: u64, changes: Vec<Change>) -> Result<Vec<Change>>;
41
42 async fn transform_rows(&self, batch_id: u64, rows: Vec<Row>) -> Result<Vec<Row>>;
44
45 async fn transform_relation_changes(
52 &self,
53 _batch_id: u64,
54 _changes: Vec<RelationChange>,
55 ) -> Result<Vec<RelationChange>> {
56 bail!(
57 "BatchTransformer::transform_relation_changes is not implemented; \
58 override it (or return Ok(changes) for explicit passthrough), or use Pipeline"
59 )
60 }
61
62 async fn transform_relations(
66 &self,
67 _batch_id: u64,
68 _relations: Vec<Relation>,
69 ) -> Result<Vec<Relation>> {
70 bail!(
71 "BatchTransformer::transform_relations is not implemented; \
72 override it (or return Ok(relations) for explicit passthrough), or use Pipeline"
73 )
74 }
75
76 async fn transform_events(
85 &self,
86 batch_id: u64,
87 events: Vec<ApplyEvent>,
88 ) -> Result<Vec<ApplyEvent>> {
89 let mut change_idxs = Vec::new();
90 let mut changes = Vec::new();
91 let mut rel_idxs = Vec::new();
92 let mut rels = Vec::new();
93 let mut out: Vec<Option<ApplyEvent>> = Vec::with_capacity(events.len());
94 for (i, event) in events.into_iter().enumerate() {
95 out.push(None);
96 match event {
97 ApplyEvent::Change(c) => {
98 change_idxs.push(i);
99 changes.push(c);
100 }
101 ApplyEvent::RelationChange(r) => {
102 rel_idxs.push(i);
103 rels.push(*r);
104 }
105 }
106 }
107
108 if !changes.is_empty() {
109 let n = changes.len();
110 let transformed = self.transform_changes(batch_id, changes).await?;
111 if transformed.len() != n {
112 bail!(
113 "transform_changes changed length ({n} → {}) in a mixed ApplyEvent batch; \
114 use homogeneous batches for filter/fan-out",
115 transformed.len()
116 );
117 }
118 for (idx, c) in change_idxs.into_iter().zip(transformed) {
119 out[idx] = Some(ApplyEvent::Change(c));
120 }
121 }
122
123 if !rels.is_empty() {
124 let n = rels.len();
125 let transformed = self.transform_relation_changes(batch_id, rels).await?;
126 if transformed.len() != n {
127 bail!(
128 "transform_relation_changes changed length ({n} → {}) in a mixed \
129 ApplyEvent batch; use homogeneous batches for filter/fan-out",
130 transformed.len()
131 );
132 }
133 for (idx, r) in rel_idxs.into_iter().zip(transformed) {
134 out[idx] = Some(ApplyEvent::relation_change(r));
135 }
136 }
137
138 Ok(out
139 .into_iter()
140 .map(|e| e.expect("every slot filled"))
141 .collect())
142 }
143}
144
145#[async_trait]
146impl BatchTransformer for Pipeline {
147 fn is_identity(&self) -> bool {
148 Pipeline::is_identity(self)
149 }
150
151 async fn transform_changes(&self, batch_id: u64, changes: Vec<Change>) -> Result<Vec<Change>> {
152 self.apply_changes_async(batch_id, changes).await
153 }
154
155 async fn transform_rows(&self, batch_id: u64, rows: Vec<Row>) -> Result<Vec<Row>> {
156 self.apply_rows_async(batch_id, rows).await
157 }
158
159 async fn transform_relation_changes(
160 &self,
161 batch_id: u64,
162 changes: Vec<RelationChange>,
163 ) -> Result<Vec<RelationChange>> {
164 self.apply_relation_changes_async(batch_id, changes).await
165 }
166
167 async fn transform_relations(
168 &self,
169 batch_id: u64,
170 relations: Vec<Relation>,
171 ) -> Result<Vec<Relation>> {
172 self.apply_relations_async(batch_id, relations).await
173 }
174
175 async fn transform_events(
176 &self,
177 batch_id: u64,
178 events: Vec<ApplyEvent>,
179 ) -> Result<Vec<ApplyEvent>> {
180 self.apply_events_async(batch_id, events).await
181 }
182}