Skip to main content

cloudillo_types/
blob_adapter.rs

1// SPDX-FileCopyrightText: Szilárd Hajba
2// SPDX-License-Identifier: LGPL-3.0-or-later
3
4//! Adapter that manages and stores blobs (immutable file data)
5use async_trait::async_trait;
6use axum::body::Bytes;
7use futures_core::Stream;
8use std::{fmt::Debug, pin::Pin};
9use tokio::io::AsyncRead;
10
11use crate::prelude::*;
12
13#[derive(Clone, Default)]
14pub struct CreateBlobOptions {}
15
16#[derive(Debug, Clone)]
17pub struct BlobStat {
18	pub size: u64,
19	/// Unix epoch seconds of the blob's last modification (or creation).
20	pub modified_at: i64,
21}
22
23#[async_trait]
24pub trait BlobAdapter: Debug + Send + Sync {
25	/// Creates a new blob from a buffer
26	async fn create_blob_buf(
27		&self,
28		tn_id: TnId,
29		file_id: &str,
30		data: &[u8],
31		opts: &CreateBlobOptions,
32	) -> ClResult<()>;
33
34	/// Creates a new blob using a stream
35	async fn create_blob_stream(
36		&self,
37		tn_id: TnId,
38		file_id: &str,
39		stream: &mut (dyn AsyncRead + Send + Unpin),
40	) -> ClResult<()>;
41
42	/// Stats a blob. Returns `None` if the blob is not present.
43	async fn stat_blob(&self, tn_id: TnId, blob_id: &str) -> Option<BlobStat>;
44
45	/// Reads a blob
46	async fn read_blob_buf(&self, tn_id: TnId, blob_id: &str) -> ClResult<Box<[u8]>>;
47
48	/// Reads a byte range from a blob as a stream (no full buffering).
49	/// `offset` is the start byte; `length` is the number of bytes to read.
50	async fn read_blob_range_stream(
51		&self,
52		tn_id: TnId,
53		blob_id: &str,
54		offset: u64,
55		length: u64,
56	) -> ClResult<Pin<Box<dyn Stream<Item = Result<Bytes, std::io::Error>> + Send>>>;
57
58	/// Reads a blob
59	async fn read_blob_stream(
60		&self,
61		tn_id: TnId,
62		blob_id: &str,
63	) -> ClResult<Pin<Box<dyn Stream<Item = Result<Bytes, std::io::Error>> + Send>>>;
64
65	/// Creates a new blob by copying a file from a local path (no memory allocation)
66	async fn create_blob_from_path(
67		&self,
68		tn_id: TnId,
69		file_id: &str,
70		source: &std::path::Path,
71		opts: &CreateBlobOptions,
72	) -> ClResult<()>;
73
74	/// Delete every blob owned by the tenant. Treats a missing tenant directory
75	/// as success. Used by tenant purge orchestration.
76	async fn delete_tenant_blobs(&self, tn_id: TnId) -> ClResult<()>;
77
78	/// Delete a single blob. Missing blob is success (idempotent).
79	async fn delete_blob(&self, tn_id: TnId, blob_id: &str) -> ClResult<()>;
80
81	/// Enumerate every `blob_id` (typically a `variant_id`) currently present
82	/// in this tenant's blob store. Used by the GC scanner.
83	async fn list_blobs(
84		&self,
85		tn_id: TnId,
86	) -> ClResult<Pin<Box<dyn Stream<Item = ClResult<String>> + Send>>>;
87
88	/// Delete in-progress upload artifacts whose mtime is at or before
89	/// `cutoff_secs` (Unix epoch seconds). These are written outside the
90	/// content-addressed `xx/yy/` shards by `create_blob_stream`, so they
91	/// are invisible to `list_blobs`. Returns the number of files removed.
92	/// Adapters with no notion of tmp files may return `Ok(0)`.
93	async fn cleanup_tmp_files(&self, tn_id: TnId, cutoff_secs: i64) -> ClResult<u64>;
94}
95
96// vim: ts=4