interface/
storage.rs

1use std::collections::HashMap;
2use std::fmt::Formatter;
3use std::io;
4use std::ops::Bound;
5use std::path::Path;
6use std::sync::Arc;
7use std::{fmt, fs};
8
9use anyhow::Result;
10
11use async_trait::async_trait;
12
13use bytes::Bytes;
14
15use futures::Stream;
16
17use serde::{Deserialize, Serialize};
18use time::OffsetDateTime;
19
20fn file_to_base64<P: AsRef<Path>>(path: P) -> Result<String> {
21    Ok(base64::encode(fs::read(path.as_ref())?))
22}
23
24#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
25#[serde(deny_unknown_fields)]
26pub struct CertificateInfo {
27    pub certificate_b64: String,
28    pub private_key_b64: String,
29}
30
31impl CertificateInfo {
32    pub fn from_path<P: AsRef<Path>, Q: AsRef<Path>>(
33        certificate_path: P,
34        private_key_path: Q,
35    ) -> Result<CertificateInfo> {
36        Ok(Self {
37            certificate_b64: file_to_base64(certificate_path)?,
38            private_key_b64: file_to_base64(private_key_path)?,
39        })
40    }
41}
42
43/// Metadata accepted when indexing a blob.
44#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq)]
45#[serde(deny_unknown_fields)]
46pub struct BlobMetaRequest {
47    /// The key/value pairs for this blob.
48    pub fields: HashMap<String, FieldValue>,
49
50    /// The tags for this blob.
51    pub tags: Vec<String>,
52}
53
54impl BlobMetaRequest {
55    pub fn new() -> Self {
56        Self::default()
57    }
58
59    #[must_use]
60    pub fn with_field<S: Into<String>, T: Into<FieldValue>>(mut self, key: S, value: T) -> Self {
61        self.fields.insert(key.into(), value.into());
62        self
63    }
64
65    #[must_use]
66    pub fn with_tag<S: Into<String>>(mut self, s: S) -> Self {
67        self.tags.push(s.into());
68        self
69    }
70
71    pub fn into_meta(
72        self,
73        created_at: OffsetDateTime,
74        modified_at: OffsetDateTime,
75        size: u64,
76    ) -> BlobMeta {
77        BlobMeta {
78            fields: self.fields,
79            tags: self.tags,
80            size,
81            created_at,
82            modified_at,
83        }
84    }
85}
86
87#[derive(Clone, Debug, Deserialize, Hash, Serialize, PartialEq, Eq)]
88#[serde(untagged)]
89pub enum FieldValue {
90    Str(String),
91    Numeric(i64),
92}
93
94#[derive(Clone, Debug, Deserialize, Hash, Serialize, PartialEq, Eq)]
95pub enum TaggedFieldValue {
96    Str(String),
97    Numeric(i64),
98}
99
100impl From<FieldValue> for TaggedFieldValue {
101    fn from(v: FieldValue) -> Self {
102        match v {
103            FieldValue::Str(s) => TaggedFieldValue::Str(s),
104            FieldValue::Numeric(i) => TaggedFieldValue::Numeric(i),
105        }
106    }
107}
108
109impl From<TaggedFieldValue> for FieldValue {
110    fn from(v: TaggedFieldValue) -> Self {
111        match v {
112            TaggedFieldValue::Str(s) => FieldValue::Str(s),
113            TaggedFieldValue::Numeric(i) => FieldValue::Numeric(i),
114        }
115    }
116}
117
118impl From<String> for FieldValue {
119    fn from(v: String) -> Self {
120        Self::Str(v)
121    }
122}
123
124impl From<&str> for FieldValue {
125    fn from(v: &str) -> Self {
126        Self::Str(String::from(v))
127    }
128}
129
130impl From<&String> for FieldValue {
131    fn from(v: &String) -> Self {
132        Self::Str(String::from(v))
133    }
134}
135
136impl From<i64> for FieldValue {
137    fn from(v: i64) -> Self {
138        Self::Numeric(v)
139    }
140}
141
142impl fmt::Display for FieldValue {
143    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
144        match self {
145            Self::Str(s) => write!(f, "\"{}\"", s),
146            Self::Numeric(i) => write!(f, "{}", i),
147        }
148    }
149}
150
151/// Metadata associated with a blob.
152#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
153#[serde(deny_unknown_fields)]
154pub struct BlobMeta {
155    /// The key/value pairs for this blob.
156    pub fields: HashMap<String, FieldValue>,
157
158    /// The tags for this blob.
159    pub tags: Vec<String>,
160
161    /// This blob's size, in bytes.
162    pub size: u64,
163
164    /// This blob's creation time.
165    #[serde(with = "time::serde::rfc3339")]
166    pub created_at: OffsetDateTime,
167
168    /// This blob's last modified time.
169    #[serde(with = "time::serde::rfc3339")]
170    pub modified_at: OffsetDateTime,
171}
172
173impl From<BlobMeta> for BlobMetaRequest {
174    fn from(m: BlobMeta) -> Self {
175        Self {
176            fields: m.fields,
177            tags: m.tags,
178        }
179    }
180}
181
182impl Default for BlobMeta {
183    fn default() -> Self {
184        Self {
185            fields: Default::default(),
186            tags: Default::default(),
187            size: 0,
188            created_at: OffsetDateTime::now_utc(),
189            modified_at: OffsetDateTime::now_utc(),
190        }
191    }
192}
193
194impl BlobMeta {
195    pub fn new() -> Self {
196        Self::default()
197    }
198
199    #[must_use]
200    pub fn with_field<S: Into<String>, T: Into<FieldValue>>(mut self, key: S, value: T) -> Self {
201        self.fields.insert(key.into(), value.into());
202        self
203    }
204
205    #[must_use]
206    pub fn with_tag<S: Into<String>>(mut self, s: S) -> Self {
207        self.tags.push(s.into());
208        self
209    }
210
211    #[must_use]
212    pub fn with_size(mut self, size: u64) -> Self {
213        self.size = size;
214        self
215    }
216}
217
218/// Tagged version of the metadata associated with a blob.
219///
220/// This is used to persist the metadata in the sled tree.
221/// bincode doesn't like untagged enums, so we have to make a tagged alternative.
222#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
223#[serde(deny_unknown_fields)]
224pub struct TaggedBlobMeta {
225    /// The key/value pairs for this blob.
226    pub fields: HashMap<String, TaggedFieldValue>,
227
228    /// The tags for this blob.
229    pub tags: Vec<String>,
230
231    /// This blob's size, in bytes.
232    pub size: u64,
233
234    /// This blob's creation time.
235    #[serde(with = "crate::timestamp_nanos")]
236    pub created_at: OffsetDateTime,
237
238    /// This blob's last modified time.
239    #[serde(with = "crate::timestamp_nanos")]
240    pub modified_at: OffsetDateTime,
241}
242
243impl From<BlobMeta> for TaggedBlobMeta {
244    fn from(m: BlobMeta) -> Self {
245        Self {
246            fields: m
247                .fields
248                .into_iter()
249                .map(|(k, v)| (k, v.into()))
250                .collect::<HashMap<_, _>>(),
251            tags: m.tags,
252            size: m.size,
253            created_at: m.created_at,
254            modified_at: m.modified_at,
255        }
256    }
257}
258
259impl From<TaggedBlobMeta> for BlobMeta {
260    fn from(m: TaggedBlobMeta) -> Self {
261        Self {
262            fields: m
263                .fields
264                .into_iter()
265                .map(|(k, v)| (k, v.into()))
266                .collect::<HashMap<_, _>>(),
267            tags: m.tags,
268            size: m.size,
269            created_at: m.created_at,
270            modified_at: m.modified_at,
271        }
272    }
273}
274
275#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
276#[serde(deny_unknown_fields)]
277pub struct BlobInfoRequest {
278    pub meta_request: BlobMetaRequest,
279    pub owner: String,
280    pub size: u64,
281}
282
283impl BlobInfoRequest {
284    pub fn into_blob_info(
285        self,
286        created_at: OffsetDateTime,
287        modified_at: OffsetDateTime,
288    ) -> BlobInfo {
289        BlobInfo {
290            meta: self
291                .meta_request
292                .into_meta(created_at, modified_at, self.size),
293            owner: self.owner,
294        }
295    }
296}
297
298#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
299#[serde(deny_unknown_fields)]
300pub struct BlobInfo {
301    pub meta: BlobMeta,
302    pub owner: String,
303}
304
305#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
306#[serde(deny_unknown_fields)]
307pub struct TaggedBlobInfo {
308    pub meta: TaggedBlobMeta,
309    pub owner: String,
310}
311
312impl From<BlobInfo> for TaggedBlobInfo {
313    fn from(v: BlobInfo) -> Self {
314        Self {
315            meta: v.meta.into(),
316            owner: v.owner,
317        }
318    }
319}
320
321impl From<TaggedBlobInfo> for BlobInfo {
322    fn from(v: TaggedBlobInfo) -> Self {
323        Self {
324            meta: v.meta.into(),
325            owner: v.owner,
326        }
327    }
328}
329
330pub struct Blob {
331    pub stream: Box<dyn Stream<Item = Result<Bytes, io::Error>> + Send>,
332    pub current_chunk_size: u64,
333    pub total_blob_size: u64,
334    pub info: BlobInfo,
335}
336
337#[async_trait]
338pub trait StorageNode {
339    async fn put(
340        &self,
341        id: String,
342        info: BlobInfoRequest,
343        stream: Option<Box<dyn Stream<Item = Result<Bytes, io::Error>> + Send + Sync + Unpin>>,
344    ) -> Result<()>;
345
346    async fn write(
347        &self,
348        id: String,
349        range: (Bound<u64>, Bound<u64>),
350        bytes: Bytes,
351        username: &str,
352    ) -> Result<()>;
353
354    async fn get(&self, blob_id: String, range: Option<(Bound<u64>, Bound<u64>)>) -> Result<Blob>;
355
356    async fn update_meta(&self, blob_id: String, info: BlobInfoRequest) -> Result<()>;
357
358    async fn delete(&self, blob_id: String, username: &str) -> Result<()>;
359
360    async fn get_certificates(&self) -> Option<CertificateInfo>;
361
362    async fn fsync(&self, blob_id: String, username: &str) -> Result<()>;
363
364    async fn flush(&self) -> Result<()>;
365}
366
367pub type DynStorageNode = Arc<dyn StorageNode + Send + Sync>;