coven_storage/provider_ops.rs
1//! Provider calls beneath the common storage contract.
2
3use crate::*;
4use coven_foundation::id_source::{DeviceId, StoreId};
5
6/// Native calls used by [`StorageConnection`]. Consumers use [`Storage`];
7/// implementations handle provider protocols after the connection checks paths,
8/// request sizes and recorded sessions. Construction receives validated settings.
9#[async_trait::async_trait]
10pub trait ProviderOps: Send + Sync {
11 /// This provider's location.
12 fn config(&self) -> StorageConfig;
13 /// Largest complete body accepted by `create` or `replace`.
14 fn single_request_limit(&self) -> u64;
15 /// The size of the parts a ranged read moves whole
16 /// ([`crate::Storage::read_part_size`]); `None` when a range moves only
17 /// its own bytes.
18 fn read_part_size(&self) -> Option<std::num::NonZeroU64> {
19 None
20 }
21 /// Create D10's location marker at the location's root with the
22 /// provider's create-once request. Only first setup at a location that
23 /// holds one store calls this operation. Anything it stages sits in
24 /// `device`'s reserved folder (D10).
25 async fn create_location_marker(
26 &self,
27 _store: coven_foundation::id_source::StoreId,
28 _device: DeviceId,
29 ) -> Result<(), StorageError> {
30 Err(StorageFailure::ProviderUnavailable.into())
31 }
32 /// Read the complete location marker, bounded to D10's UUID byte count.
33 /// Only a location that holds one store calls this operation. A marker
34 /// read that finds nothing confirms the location is still there first.
35 async fn read_location_marker(&self) -> Result<Vec<u8>, StorageError> {
36 Err(StorageFailure::ProviderUnavailable.into())
37 }
38 /// The store's folder's name and web address as this connection's
39 /// account names them now (§20.2). Only a Dropbox shared folder or a
40 /// OneDrive folder has them; nothing asks another location.
41 async fn folder_label(&self) -> Result<FolderLabel, StorageError> {
42 Err(StorageFailure::ProviderUnavailable.into())
43 }
44 /// Signed-in account; coven shares nothing on S3, so it has none.
45 async fn account(&self) -> Result<String, StorageError> {
46 Err(StorageFailure::InvalidConfiguration.with_source("provider has no sharing account"))
47 }
48 /// Replace tokens after credential custody commits them.
49 async fn set_oauth_tokens(&self, _tokens: OAuthTokens) -> Result<(), StorageError> {
50 Err(StorageFailure::InvalidConfiguration.with_source("provider does not use OAuth"))
51 }
52 /// Replace the S3 signing key after credential custody commits it.
53 async fn set_s3_credentials(&self, _credentials: S3Credentials) -> Result<(), StorageError> {
54 Err(StorageFailure::InvalidConfiguration.with_source("provider does not use S3 keys"))
55 }
56 /// Create in one request, telling `sent` the body's bytes as the
57 /// provider's client hands them over. A temporary it stages sits in
58 /// `device`'s reserved folder (D10).
59 /// Returns the created object's metadata when the reply carries it.
60 async fn create(
61 &self,
62 publication: &PublicationRequest,
63 path: &ObjectPath,
64 bytes: &[u8],
65 sent: &ByteProgress,
66 device: DeviceId,
67 ) -> Result<Option<StoredObject>, StorageError>;
68 /// Replace the observed object in one atomic conditional publication. A
69 /// temporary it stages sits in `device`'s reserved folder (D10).
70 async fn replace(
71 &self,
72 publication: &PublicationRequest,
73 previous: &StoredObject,
74 bytes: &[u8],
75 device: DeviceId,
76 ) -> Result<(), StorageError>;
77 /// Exact native lookup; only a confirmed missing object returns None.
78 async fn status(&self, path: &ObjectPath) -> Result<Option<StoredObject>, StorageError>;
79 /// Read complete bytes or a range, retaining metadata for those exact bytes.
80 /// The connection hears the stream's reads itself; a client that fetches
81 /// bytes before the stream yields them tells `received` how far through
82 /// the object they reach as they arrive, counted from the object's start.
83 async fn stream_object(
84 &self,
85 path: &ObjectPath,
86 range: Option<ByteRange>,
87 received: &ByteProgress,
88 ) -> Result<crate::ObjectStream<'_>, StorageError>;
89 /// Collect a bounded response through the streaming implementation. Large
90 /// writes and snapshots consume `stream_object` directly. This convenience
91 /// reader holds at most 32 MiB, regardless of provider metadata.
92 async fn read_object(
93 &self,
94 path: &ObjectPath,
95 range: Option<ByteRange>,
96 ) -> Result<ObjectRead, StorageError> {
97 self.stream_object(path, range, &ByteProgress::unread())
98 .await?
99 .collect(crate::object_stream::BUFFERED_READ_LIMIT)
100 .await
101 }
102 /// Consume native pages into the connection's checked listing.
103 async fn list(&self, listing: &mut ObjectListing) -> Result<(), StorageError>;
104
105 /// Delete, accepting an already absent object.
106 async fn delete(&self, path: &ObjectPath) -> Result<(), StorageError>;
107 /// Grant account access, or return S3's console instructions.
108 async fn grant_access(&self, account: &str) -> Result<AccessGrant, StorageError>;
109 /// Accept native sharing material. The connection checks location and reads
110 /// the exact floor status afterwards to verify readability.
111 async fn join(&self, invitation: &StorageInvitation) -> Result<(), StorageError> {
112 if !matches!(invitation.acceptance, crate::invitation::InvitationAcceptance::Granted) {
113 return Err(
114 StorageFailure::InvalidConfiguration.with_source("provider acceptance required")
115 );
116 }
117 Ok(())
118 }
119 /// Undo [`Self::join`]'s acceptance so the invite stays usable
120 /// ([`crate::Storage::leave`]). Access an invite grants without native
121 /// acceptance has nothing to undo.
122 async fn leave(&self, invitation: &StorageInvitation) -> Result<(), StorageError> {
123 if !matches!(invitation.acceptance, crate::invitation::InvitationAcceptance::Granted) {
124 return Err(
125 StorageFailure::InvalidConfiguration.with_source("provider acceptance required")
126 );
127 }
128 Ok(())
129 }
130 /// Remove exclusive grants and return grants that still reach the account.
131 async fn revoke_access(&self, account: &str) -> Result<Vec<RetainedAccess>, StorageError>;
132 /// Begin a nonempty upload with its original publication condition, for
133 /// `device`, whose reserved folder holds its temporaries (D10).
134 async fn begin_upload(
135 &self,
136 publication: &PublicationRequest,
137 target: &UploadTarget,
138 total: u64,
139 device: DeviceId,
140 ) -> Result<UploadSession, StorageError>;
141 /// Recover native progress of a validated, incomplete session.
142 async fn resume_upload(
143 &self,
144 publication: &PublicationRequest,
145 session: &mut UploadSession,
146 ) -> Result<(), StorageError>;
147 /// Send a checked part ending at `end`. The connection validates its length,
148 /// boundary and session; implementations enforce the native protocol.
149 /// `sent` already counts from the part's start in the object.
150 async fn upload_part(
151 &self,
152 publication: &PublicationRequest,
153 session: &mut UploadSession,
154 bytes: &[u8],
155 end: u64,
156 sent: &ByteProgress,
157 ) -> Result<(), StorageError>;
158 /// Send part `offset..` of a session whose parts are independent
159 /// ([`UploadSession::parts_independent`]), leaving the session as it
160 /// is: the provider's confirmation of the part comes back, for the
161 /// caller to add. Several may be in flight at once. The connection
162 /// checks the part is a whole one the session lacks; `sent` already
163 /// counts from the part's start in the object. A provider whose parts
164 /// go in order has none to send this way.
165 async fn send_independent_part(
166 &self,
167 _publication: &PublicationRequest,
168 _session: &UploadSession,
169 _offset: u64,
170 _bytes: &[u8],
171 _sent: &ByteProgress,
172 ) -> Result<ConfirmedPart, StorageError> {
173 Err(StorageFailure::InvalidPart.into())
174 }
175 /// Publish an incomplete session, checking native progress where required.
176 async fn finish_upload(
177 &self,
178 publication: &PublicationRequest,
179 session: &mut UploadSession,
180 ) -> Result<(), StorageError>;
181 /// Abandon an incomplete session without deleting published bytes.
182 async fn abort_upload(
183 &self,
184 publication: &PublicationRequest,
185 session: &UploadSession,
186 ) -> Result<(), StorageError>;
187 /// The uploads `device` left in `store` that this sign-in can see: the
188 /// sessions and temporaries in its reserved folder (D10), and, where the
189 /// provider puts a session at its destination, those under the device's
190 /// file and snapshot folders. Every page of each listing is followed.
191 async fn device_uploads(
192 &self,
193 store: StoreId,
194 device: DeviceId,
195 ) -> Result<Vec<LeftUpload>, StorageError>;
196 /// The unfinished sessions at slots 1 to `through` of `store`, whoever
197 /// began them: where a claim's session sits at its slot's path, one at
198 /// a slot this device has applied can never publish, since create-once
199 /// refuses it there. A provider that puts no session at a slot's path
200 /// returns none.
201 async fn settled_claim_uploads(
202 &self,
203 store: StoreId,
204 through: u64,
205 ) -> Result<Vec<LeftUpload>, StorageError>;
206 /// Whether this provider dates an unfinished floor session's start so
207 /// that one begun before the current floor's publication can be told
208 /// apart (Appendix E.10). Asking sends nothing.
209 fn dates_floor_sessions(&self) -> bool {
210 false
211 }
212 /// The unfinished sessions at the floor's path begun before `floor`, the
213 /// current floor, was published: each observed an older revision, so
214 /// none can publish. Asked only where [`Self::dates_floor_sessions`].
215 async fn superseded_floor_uploads(
216 &self,
217 _floor: &StoredObject,
218 ) -> Result<Vec<LeftUpload>, StorageError> {
219 Ok(Vec::new())
220 }
221 /// What S3 detection run `run` left in `store`'s `.coven-check` prefix
222 /// (§20.7): its unfinished multipart uploads, then its probes. Only S3
223 /// detects, so elsewhere no run left anything.
224 async fn detection_leftovers(
225 &self,
226 _store: StoreId,
227 _run: uuid::Uuid,
228 ) -> Result<Vec<LeftUpload>, StorageError> {
229 Ok(Vec::new())
230 }
231 /// End one upload another device left, as this provider listed it: abort
232 /// the session, or delete what it staged. One already ended succeeds,
233 /// and a published object is never touched.
234 async fn end_left_upload(&self, upload: &LeftUpload) -> Result<(), StorageError>;
235}