eternalfest_core/
blob.rs

1use crate::core::Instant;
2use crate::digest::{DigestSha2, DigestSha3};
3#[cfg(feature = "serde")]
4use crate::serde_buffer::{buffer_to_hex, hex_to_buffer};
5use async_trait::async_trait;
6use auto_impl::auto_impl;
7use etwin_core::types::AnyError;
8use etwin_core::{declare_new_string, declare_new_uuid};
9#[cfg(feature = "serde")]
10use etwin_serde_tools::{Deserialize, Serialize};
11use std::collections::{HashMap, HashSet};
12use std::ops::Range;
13
14declare_new_uuid! {
15  pub struct BlobId(Uuid);
16  pub type ParseError = BlobIdParseError;
17  const SQL_NAME = "blob_id";
18}
19
20#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
21#[cfg_attr(feature = "serde", serde(tag = "type", rename = "Blob"))]
22#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
23pub struct BlobIdRef {
24  pub id: BlobId,
25}
26
27impl BlobIdRef {
28  pub const fn new(id: BlobId) -> Self {
29    Self { id }
30  }
31}
32
33impl From<BlobId> for BlobIdRef {
34  fn from(id: BlobId) -> Self {
35    Self::new(id)
36  }
37}
38
39declare_new_uuid! {
40  pub struct UploadSessionId(Uuid);
41  pub type ParseError = UploadSessionIdParseError;
42  const SQL_NAME = "upload_session_id";
43}
44
45declare_new_string! {
46  pub struct MediaType(String);
47  pub type ParseError = MediaTypeParseError;
48  const PATTERN = r"^[0-9a-z.-]{1,100}/[0-9a-z.-]{1,100}$";
49  const SQL_NAME = "media_type";
50}
51
52#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
53#[cfg_attr(feature = "serde", serde(tag = "type", rename = "Blob"))]
54#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
55pub struct Blob {
56  pub id: BlobId,
57  pub media_type: MediaType,
58  pub byte_size: u32,
59  pub digest: BlobDigest,
60}
61
62impl Blob {
63  pub const fn as_ref(&self) -> BlobIdRef {
64    BlobIdRef::new(self.id)
65  }
66}
67
68#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
69#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
70pub struct BlobDigest {
71  pub sha2_256: DigestSha2,
72  pub sha3_256: DigestSha3,
73}
74
75impl BlobDigest {
76  pub fn digest(data: &[u8]) -> Self {
77    Self {
78      sha2_256: DigestSha2::digest(data),
79      sha3_256: DigestSha3::digest(data),
80    }
81  }
82}
83
84#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
85#[derive(Clone, Debug, PartialEq, Eq, Hash)]
86pub struct UploadSession {
87  pub id: UploadSessionId,
88  pub expires_at: Instant,
89  pub remaining_range: Range<u32>,
90  pub blob: Option<Blob>,
91}
92
93#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
94#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
95pub struct CreateBlobOptions {
96  pub media_type: MediaType,
97  #[cfg_attr(
98    feature = "serde",
99    serde(serialize_with = "buffer_to_hex", deserialize_with = "hex_to_buffer")
100  )]
101  pub data: Vec<u8>,
102}
103
104#[derive(Debug, thiserror::Error)]
105pub enum CreateBlobError {
106  #[error("Blob size exceeds maximum allowed size")]
107  MaxSize,
108  #[error(transparent)]
109  Other(#[from] AnyError),
110}
111
112impl CreateBlobError {
113  pub fn other<E>(e: E) -> Self
114  where
115    E: ::std::error::Error + Send + Sync + 'static,
116  {
117    CreateBlobError::Other(Box::new(e))
118  }
119}
120
121#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
122#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
123pub struct GetBlobOptions {
124  pub id: BlobId,
125}
126
127#[derive(Debug, thiserror::Error)]
128pub enum GetBlobError {
129  #[error("Failed to find blob: {:?}", .0)]
130  NotFound(BlobId),
131  #[error(transparent)]
132  Other(#[from] AnyError),
133}
134
135#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
136#[derive(Clone, Debug, PartialEq, Eq)]
137pub struct GetBlobsOptions {
138  pub id: HashSet<BlobId>,
139  pub now: Instant,
140  pub time: Option<Instant>,
141}
142
143#[derive(Debug, thiserror::Error)]
144pub enum GetBlobsError {
145  #[error(transparent)]
146  Other(#[from] AnyError),
147}
148
149#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
150#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
151pub struct GetBlobDataOptions {
152  pub id: BlobId,
153}
154
155#[derive(Debug, thiserror::Error)]
156pub enum GetBlobDataError {
157  #[error("Failed to find blob: {:?}", .0)]
158  NotFound(BlobId),
159  #[error(transparent)]
160  Other(#[from] AnyError),
161}
162
163#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
164#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
165pub struct CreateUploadSessionOptions {
166  pub media_type: MediaType,
167  pub byte_size: u32,
168}
169
170#[derive(Debug, thiserror::Error)]
171pub enum CreateUploadSessionError {
172  #[error("Upload session blob size exceeds maximum")]
173  MaxSize,
174  #[error(transparent)]
175  Other(#[from] AnyError),
176}
177
178impl CreateUploadSessionError {
179  pub fn other<E>(e: E) -> Self
180  where
181    E: ::std::error::Error + Send + Sync + 'static,
182  {
183    Self::Other(Box::new(e))
184  }
185}
186
187#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
188#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
189pub struct UploadOptions {
190  pub upload_session_id: UploadSessionId,
191  pub offset: u32,
192  #[cfg_attr(
193    feature = "serde",
194    serde(serialize_with = "buffer_to_hex", deserialize_with = "hex_to_buffer")
195  )]
196  pub data: Vec<u8>,
197}
198
199#[derive(Debug, thiserror::Error)]
200pub enum UploadError {
201  #[error("Failed to find upload session for id: {:?}", .0)]
202  NotFound(UploadSessionId),
203  #[error("Upload session expired: {:?}", .0)]
204  Expired(UploadSessionId),
205  #[error("Upload session expected data from offset {} but received from offset {}", .expected, .actual)]
206  BadOffset { actual: u32, expected: u32 },
207  #[error("Upload session tried to write past the reserved size")]
208  Overflow,
209  #[error("Upload session tried to upload an empty chunk")]
210  EmptyInputData,
211  #[error(transparent)]
212  Other(#[from] AnyError),
213}
214
215impl UploadError {
216  pub fn other<E>(e: E) -> Self
217  where
218    E: ::std::error::Error + Send + Sync + 'static,
219  {
220    Self::Other(Box::new(e))
221  }
222}
223
224#[async_trait]
225#[auto_impl(&, Arc)]
226pub trait BlobStore: Send + Sync {
227  fn has_immutable_blobs(&self) -> bool;
228
229  async fn create_blob(&self, options: &CreateBlobOptions) -> Result<Blob, CreateBlobError>;
230
231  async fn get_blob(&self, options: &GetBlobOptions) -> Result<Blob, GetBlobError>;
232
233  async fn get_blobs(
234    &self,
235    options: &GetBlobsOptions,
236  ) -> Result<HashMap<BlobId, Result<Blob, GetBlobError>>, GetBlobsError>;
237
238  async fn get_blob_data(&self, options: &GetBlobDataOptions) -> Result<Vec<u8>, GetBlobDataError>;
239
240  async fn create_upload_session(
241    &self,
242    options: &CreateUploadSessionOptions,
243  ) -> Result<UploadSession, CreateUploadSessionError>;
244
245  async fn upload(&self, options: &UploadOptions) -> Result<UploadSession, UploadError>;
246}