1mod sealed {
15 pub trait Sealed {}
16}
17
18use std::time::SystemTime;
24
25use serde::Serialize;
27
28use super::backend::{BackendSnapshot, ScanPage, StorageBackend};
30use super::manager::Operation;
31use super::query::{FileSelection, ObjectSelection, RecordSelection, SchemaSelection, StreamSelection};
32use super::{FileReference, ObjectEntry, StorageCursor, StorageFuture, StorageSchema, StoredRecord, StoredRow};
33use crate::Identifier;
34use harmos_domain::model::storage::{IdentitySelection, StoredIdentity};
35
36pub struct Queued<'a>(&'a Operation);
45
46pub trait Entry: Clone + Serialize + Send + Sync + 'static + sealed::Sealed {
51 type Selection: Send + Sync;
52
53 fn position(&self) -> &StorageCursor;
54 fn matches(&self, selection: &Self::Selection) -> bool;
55
56 fn start(_selection: &Self::Selection) -> u64 {
57 0
58 }
59
60 fn pending(queued: Queued<'_>) -> &[Self];
61 fn scan<'a>(
62 backend: &'a dyn StorageBackend, snapshot: &'a BackendSnapshot, selection: &'a Self::Selection, page: ScanPage,
63 ) -> StorageFuture<'a, Vec<Self>>;
64}
65
66impl<'a> Queued<'a> {
69 pub(super) fn new(operation: &'a Operation) -> Self {
70 Self(operation)
71 }
72}
73
74impl sealed::Sealed for ObjectEntry {}
75impl sealed::Sealed for StoredRecord {}
76impl sealed::Sealed for StoredRow {}
77impl sealed::Sealed for StorageSchema {}
78impl sealed::Sealed for FileReference {}
79impl sealed::Sealed for StoredIdentity {}
80
81impl Entry for ObjectEntry {
82 type Selection = ObjectSelection;
83
84 fn position(&self) -> &StorageCursor {
85 &self.position
86 }
87
88 fn matches(&self, selection: &ObjectSelection) -> bool {
90 self.version.check_position(&self.position).is_ok()
91 && selection.schema.as_ref().is_none_or(|key| key == &self.value.schema)
92 && selection.key.as_ref().is_none_or(|key| key == &self.value.item)
93 }
94
95 fn pending(queued: Queued<'_>) -> &[Self] {
96 match queued.0 {
97 Operation::ObjectGroup { objects, .. } => objects,
98 _ => &[],
99 }
100 }
101
102 fn scan<'a>(
103 backend: &'a dyn StorageBackend, snapshot: &'a BackendSnapshot, selection: &'a ObjectSelection, page: ScanPage,
104 ) -> StorageFuture<'a, Vec<Self>> {
105 backend.read_objects(snapshot, selection, page)
106 }
107}
108
109impl Entry for StoredRecord {
110 type Selection = RecordSelection;
111
112 fn position(&self) -> &StorageCursor {
113 &self.position
114 }
115
116 fn matches(&self, selection: &RecordSelection) -> bool {
117 self.schema == selection.schema
118 && optional(&selection.scope, self.scope.as_ref())
119 && optional(&selection.item, Some(&self.item))
120 && within(selection.source_start, selection.source_end, self.source_time)
121 }
122
123 fn pending(queued: Queued<'_>) -> &[Self] {
124 match queued.0 {
125 Operation::Records(values) => values,
126 _ => &[],
127 }
128 }
129
130 fn scan<'a>(
131 backend: &'a dyn StorageBackend, snapshot: &'a BackendSnapshot, selection: &'a RecordSelection, page: ScanPage,
132 ) -> StorageFuture<'a, Vec<Self>> {
133 backend.read_records(snapshot, selection, page)
134 }
135}
136
137impl Entry for StoredRow {
138 type Selection = StreamSelection;
139
140 fn position(&self) -> &StorageCursor {
141 &self.record.position
142 }
143
144 fn matches(&self, selection: &StreamSelection) -> bool {
145 let record = &self.record;
146 record.schema == selection.schema
147 && record.position.sequence > selection.after
148 && optional(&selection.scope, record.scope.as_ref())
149 && optional(&selection.item, Some(&record.item))
150 && optional(&selection.stream, Some(&self.stream))
151 && within(selection.source_start, selection.source_end, record.source_time)
152 }
153
154 fn start(selection: &StreamSelection) -> u64 {
155 selection.after
156 }
157
158 fn pending(queued: Queued<'_>) -> &[Self] {
159 match queued.0 {
160 Operation::Stream(values) => values,
161 _ => &[],
162 }
163 }
164
165 fn scan<'a>(
166 backend: &'a dyn StorageBackend, snapshot: &'a BackendSnapshot, selection: &'a StreamSelection, page: ScanPage,
167 ) -> StorageFuture<'a, Vec<Self>> {
168 backend.read_stream(snapshot, selection, page)
169 }
170}
171
172impl Entry for StorageSchema {
173 type Selection = SchemaSelection;
174
175 fn position(&self) -> &StorageCursor {
176 &self.position
177 }
178
179 fn matches(&self, selection: &SchemaSelection) -> bool {
180 selection.schema.as_ref().is_none_or(|key| key == &self.key)
181 }
182
183 fn pending(queued: Queued<'_>) -> &[Self] {
184 match queued.0 {
185 Operation::Schema(value) => std::slice::from_ref(value),
186 Operation::ObjectGroup { schemas, .. } => schemas,
187 _ => &[],
188 }
189 }
190
191 fn scan<'a>(
192 backend: &'a dyn StorageBackend, snapshot: &'a BackendSnapshot, selection: &'a SchemaSelection, page: ScanPage,
193 ) -> StorageFuture<'a, Vec<Self>> {
194 backend.read_schemas(snapshot, selection, page)
195 }
196}
197
198impl Entry for FileReference {
199 type Selection = FileSelection;
200
201 fn position(&self) -> &StorageCursor {
202 &self.position
203 }
204
205 fn matches(&self, selection: &FileSelection) -> bool {
206 optional(&selection.scope, self.metadata.scope.as_ref()) && optional(&selection.item, Some(&self.metadata.id))
207 }
208
209 fn pending(queued: Queued<'_>) -> &[Self] {
210 match queued.0 {
211 Operation::File(value) => std::slice::from_ref(value),
212 _ => &[],
213 }
214 }
215
216 fn scan<'a>(
217 backend: &'a dyn StorageBackend, snapshot: &'a BackendSnapshot, selection: &'a FileSelection, page: ScanPage,
218 ) -> StorageFuture<'a, Vec<Self>> {
219 backend.read_files(snapshot, selection, page)
220 }
221}
222
223impl Entry for StoredIdentity {
225 type Selection = IdentitySelection;
226
227 fn position(&self) -> &StorageCursor {
228 self.first()
229 }
230
231 fn matches(&self, selection: &IdentitySelection) -> bool {
232 selection.matches(self)
233 }
234
235 fn pending(_: Queued<'_>) -> &[Self] {
236 &[]
237 }
238
239 fn scan<'a>(
240 backend: &'a dyn StorageBackend, snapshot: &'a BackendSnapshot, selection: &'a IdentitySelection, page: ScanPage,
241 ) -> StorageFuture<'a, Vec<Self>> {
242 backend.read_identities(snapshot, selection, page)
243 }
244}
245
246fn optional(expected: &Option<Identifier>, actual: Option<&Identifier>) -> bool {
251 expected.as_ref().is_none_or(|expected| Some(expected) == actual)
252}
253
254fn within(start: Option<SystemTime>, end: Option<SystemTime>, time: Option<SystemTime>) -> bool {
256 match (start, end) {
257 (Some(start), Some(end)) => time.is_some_and(|time| time >= start && time < end),
258 _ => true,
259 }
260}
261
262