aster_forge_cloud_files_core/change.rs
1//! Incremental backend changes, deletion tombstones, and explicit cursor resets.
2
3use crate::{ChangeCursor, CloudItem, CloudItemId, CloudItemKey, MetadataRevision};
4
5/// One product-neutral remote change.
6#[derive(Debug, Clone, PartialEq, Eq)]
7pub enum CloudChange {
8 /// Creates an item or replaces its known metadata/content state.
9 Upsert {
10 /// Complete current item state.
11 item: CloudItem,
12 },
13 /// Removes an item using a durable deletion tombstone.
14 Delete {
15 /// Fully scoped stable identity of the deleted item.
16 key: CloudItemKey,
17 /// Last known parent identity, when supplied by the backend or local baseline.
18 previous_parent_id: Option<CloudItemId>,
19 /// Deletion/metadata revision, when the backend exposes one.
20 metadata_revision: Option<MetadataRevision>,
21 },
22}
23
24impl CloudChange {
25 /// Returns the fully scoped item identity affected by this change.
26 #[must_use]
27 pub const fn key(&self) -> &CloudItemKey {
28 match self {
29 Self::Upsert { item } => item.key(),
30 Self::Delete { key, .. } => key,
31 }
32 }
33}
34
35/// One durable batch from an anchored backend change stream.
36#[derive(Debug, Clone, PartialEq, Eq)]
37pub struct ChangeBatch {
38 changes: Vec<CloudChange>,
39 next_cursor: ChangeCursor,
40 has_more: bool,
41}
42
43impl ChangeBatch {
44 /// Creates a change batch. The cursor becomes active only after effects are durably replayable.
45 #[must_use]
46 pub const fn new(changes: Vec<CloudChange>, next_cursor: ChangeCursor, has_more: bool) -> Self {
47 Self {
48 changes,
49 next_cursor,
50 has_more,
51 }
52 }
53
54 /// Returns changes in backend-defined application order.
55 #[must_use]
56 pub fn changes(&self) -> &[CloudChange] {
57 &self.changes
58 }
59
60 /// Returns the checkpoint following this batch.
61 #[must_use]
62 pub const fn next_cursor(&self) -> &ChangeCursor {
63 &self.next_cursor
64 }
65
66 /// Returns whether another batch is immediately available.
67 #[must_use]
68 pub const fn has_more(&self) -> bool {
69 self.has_more
70 }
71
72 /// Consumes the batch and returns its changes, next cursor, and continuation flag.
73 #[must_use]
74 pub fn into_parts(self) -> (Vec<CloudChange>, ChangeCursor, bool) {
75 (self.changes, self.next_cursor, self.has_more)
76 }
77}
78
79/// Reason an existing change checkpoint can no longer continue incrementally.
80#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
81pub enum ChangeResetReason {
82 /// The backend no longer retains changes after the supplied cursor.
83 CursorExpired,
84 /// The backend rebuilt or replaced its change-tracking state.
85 BackendStateRebuilt,
86 /// Stable item identifiers or their mapping can no longer be trusted.
87 IdentityMappingInvalidated,
88}
89
90/// Result of requesting the next anchored change page.
91#[derive(Debug, Clone, PartialEq, Eq)]
92pub enum ChangePage {
93 /// A replayable batch and its following checkpoint.
94 Batch(ChangeBatch),
95 /// Incremental continuation stopped and requires reconciliation or reimport.
96 ResetRequired {
97 /// Product-neutral reset classification.
98 reason: ChangeResetReason,
99 },
100}