coven_database/write_batch.rs
1//! The app bytes one staging writes to coven's copies of them, each file
2//! given its new id (§17.2).
3
4use coven_foundation::id_source::{FileId, IdSource};
5use std::pin::Pin;
6use tokio::io::AsyncRead;
7
8/// App bytes to stage, before the write that puts them in rows; a failure
9/// discards all of them. Each staged file gets a new id from the store's id
10/// source, which the batch borrows while the app stages; the write's SQL
11/// puts the id in the rows that reference the file.
12pub struct WriteBatch<'a> {
13 ids: &'a dyn IdSource,
14 files: Vec<BatchFile>,
15}
16
17/// A new file over an existing file's bytes: its new id, and the file whose
18/// bytes, namespace, place and key it takes (§17.2).
19#[derive(Clone, Copy, Debug)]
20pub(crate) struct ExistingFile {
21 pub(crate) id: FileId,
22 pub(crate) from: FileId,
23}
24
25/// One staged file: its id, its namespace, its bytes, and where it is to be.
26pub(crate) struct BatchFile {
27 pub(crate) id: FileId,
28 pub(crate) namespace: String,
29 pub(crate) source: FileSource,
30 pub(crate) place: Place,
31}
32
33/// Where a staged file is to be: on this device only, or uploaded to the
34/// cloud (§17.3).
35#[derive(Clone, Copy, Debug, PartialEq, Eq)]
36pub enum Place {
37 /// On this device only, with no upload.
38 DeviceOnly,
39 /// Uploaded; on this device only until its upload is stored.
40 Cloud,
41 /// Uploaded, as `Cloud`, and kept whole here too once stored: the write
42 /// switching it to the cloud pins it on this device, which makes its
43 /// cache copy from coven's copy of app bytes, and downloads a user's
44 /// original's from storage, never reading the original (§17.8).
45 CloudKeptHere,
46}
47
48impl<'a> WriteBatch<'a> {
49 pub(crate) fn new(ids: &'a dyn IdSource) -> Self {
50 Self { ids, files: Vec::new() }
51 }
52
53 /// The files staged, for the staging that copies them in.
54 pub(crate) fn into_files(self) -> Vec<BatchFile> {
55 self.files
56 }
57
58 /// Stage app bytes as a new file in an app namespace, kept in coven's
59 /// own copy: on this device only, or for the cloud. A stream is consumed
60 /// once and never buffered whole. Returns the file's id for a row to
61 /// reference.
62 pub fn put_file(
63 &mut self,
64 namespace: &str,
65 bytes: impl Into<FileSource>,
66 place: Place,
67 ) -> FileId {
68 let id = FileId(self.ids.new_id());
69 self.files.push(BatchFile {
70 id,
71 namespace: namespace.to_owned(),
72 source: bytes.into(),
73 place,
74 });
75 id
76 }
77}
78
79/// An owned buffer or a reader whose stream coven consumes exactly once.
80pub enum FileSource {
81 /// Bytes already held by the app.
82 Bytes(Vec<u8>),
83 /// A stream read in bounded chunks.
84 Stream(Pin<Box<dyn AsyncRead + Send>>),
85}
86
87impl From<Vec<u8>> for FileSource {
88 fn from(bytes: Vec<u8>) -> Self {
89 Self::Bytes(bytes)
90 }
91}