base_http_content/upload_store.rs
1// Released under the MIT License.
2// Copyright, 2026, by Samuel Williams.
3
4use base_scheduler::Cancellation;
5use base_web_content::UploadHead;
6use std::{error::Error, fmt, future::Future};
7
8/// Storage for uploads, which [`read_content`](crate::read_content) streams each upload into as it arrives.
9///
10/// Each upload is created, written in pieces, and then finished, which gives the handle the declared struct holds in its [`Upload`](base_web_content::Upload). An upload that fails, or whose content stops arriving, is aborted, and a finished upload whose content is refused is removed. [`FileStore`](crate::FileStore) stores uploads in files.
11///
12/// Uploads are stored before the whole body has been validated, so a store must not make an upload visible to anything else until the application accepts it.
13pub trait UploadStore: Send {
14 /// What the declared struct holds for a stored upload, such as a path.
15 type Handle: Send;
16 /// An upload being stored.
17 type Upload: Send;
18 /// Why a store operation failed.
19 type Error: Error + Send + Sync + 'static;
20
21 /// Begin storing an upload. When this fails, nothing of the upload is stored.
22 ///
23 /// The head's filename and media type are what the client declared, and must not choose where the upload is stored.
24 fn create(
25 &mut self,
26 head: &UploadHead,
27 cancellation: &Cancellation,
28 ) -> impl Future<Output = Result<Self::Upload, Self::Error>> + Send;
29
30 /// Store some of an upload's content, after what was stored before. When this fails, the caller must abort the upload, as [`read_content`](crate::read_content) does.
31 fn write(
32 &mut self,
33 upload: &mut Self::Upload,
34 content: &[u8],
35 cancellation: &Cancellation,
36 ) -> impl Future<Output = Result<(), Self::Error>> + Send;
37
38 /// Complete an upload whose content has all been written, returning its handle. When this fails, the caller must abort the upload, as [`read_content`](crate::read_content) does.
39 fn finish(
40 &mut self,
41 upload: &mut Self::Upload,
42 cancellation: &Cancellation,
43 ) -> impl Future<Output = Result<Self::Handle, Self::Error>> + Send;
44
45 /// Remove whatever was stored of an upload that was not finished, or whose finish failed. It is not cancellable, since it cleans up after a failure.
46 fn abort(
47 &mut self,
48 upload: Self::Upload,
49 ) -> impl Future<Output = Result<(), Self::Error>> + Send;
50
51 /// Remove a finished upload. It is not cancellable, since it cleans up after a failure.
52 fn remove(
53 &mut self,
54 handle: Self::Handle,
55 ) -> impl Future<Output = Result<(), Self::Error>> + Send;
56}
57
58/// A store for declarations without uploads, which refuses any upload.
59///
60/// A declaration without uploads accepts handles of every type, so it can be read with this store, whose handle is `()`. If the declaration does have uploads of `Upload<()>`, an upload fails to store with [`NoUploadsError`].
61#[derive(Clone, Copy, Debug, Default)]
62pub struct NoUploads;
63
64/// An upload reached [`NoUploads`].
65#[derive(Clone, Copy, Debug, PartialEq, Eq)]
66pub struct NoUploadsError;
67
68impl fmt::Display for NoUploadsError {
69 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
70 formatter.write_str("no store was given for uploads")
71 }
72}
73
74impl Error for NoUploadsError {}
75
76impl UploadStore for NoUploads {
77 type Handle = ();
78 type Upload = ();
79 type Error = NoUploadsError;
80
81 async fn create(&mut self, _: &UploadHead, _: &Cancellation) -> Result<(), NoUploadsError> {
82 Err(NoUploadsError)
83 }
84
85 async fn write(
86 &mut self,
87 _: &mut (),
88 _: &[u8],
89 _: &Cancellation,
90 ) -> Result<(), NoUploadsError> {
91 Err(NoUploadsError)
92 }
93
94 async fn finish(&mut self, _: &mut (), _: &Cancellation) -> Result<(), NoUploadsError> {
95 Err(NoUploadsError)
96 }
97
98 async fn abort(&mut self, _: ()) -> Result<(), NoUploadsError> {
99 Ok(())
100 }
101
102 async fn remove(&mut self, _: ()) -> Result<(), NoUploadsError> {
103 Ok(())
104 }
105}