use myrmic_common::db::{BlobPath, ChunkRange, Scope};
use crate::db::blob;
use crate::{Result, String, Vec};
const REQUEST_BUFFER: usize = 1024;
pub struct BlobStore {
scope: Scope,
}
impl BlobStore {
#[must_use]
pub fn new(scope: Scope) -> Self {
Self { scope }
}
#[must_use]
pub fn scope(&self) -> &Scope {
&self.scope
}
pub fn upload<'a, I, S, B>(&self, iter: I) -> Result
where
I: IntoIterator<Item = &'a (S, B)>,
S: AsRef<str> + 'a,
B: AsRef<[u8]> + 'a,
{
for (path, bytes) in iter.into_iter() {
self.put(path.as_ref(), bytes.as_ref())?;
}
Ok(())
}
pub fn put(&self, path: &str, bytes: &[u8]) -> Result {
let mut req = [0u8; REQUEST_BUFFER];
let mut rsp = [0u8; REQUEST_BUFFER];
let blob_id = blob::blob_store(self.scope.clone(), bytes, &mut req, &mut rsp)
.map_err(|_| "BlobStore::put (store)")?;
blob::blob_link(blob_id, normalize(path), &mut req).map_err(|_| "BlobStore::put (link)")
}
pub fn get(&self, path: &str) -> Result<Option<Vec<u8>>> {
let path = normalize(path);
let Some(size) = self.size_of(&path)? else {
return Ok(None);
};
let mut req = [0u8; REQUEST_BUFFER];
let mut rsp = crate::vec![0u8; size as usize + REQUEST_BUFFER];
let response = blob::path_resolve(self.scope.clone(), path, None, &mut req, &mut rsp)
.map_err(|_| "BlobStore::get")?;
Ok(response.map(|response| response.blob))
}
pub fn size_of(&self, path: &str) -> Result<Option<u64>> {
let mut req = [0u8; REQUEST_BUFFER];
let mut rsp = [0u8; REQUEST_BUFFER];
let probe = ChunkRange {
offset: 0,
length: 0,
};
let response = blob::path_resolve(
self.scope.clone(),
normalize(path),
Some(probe),
&mut req,
&mut rsp,
)
.map_err(|_| "BlobStore::size_of")?;
Ok(response.map(|response| response.total_len))
}
pub fn get_range(&self, path: &str, offset: u64, length: u64) -> Result<Option<Vec<u8>>> {
let mut req = [0u8; REQUEST_BUFFER];
let mut rsp = crate::vec![0u8; length as usize + REQUEST_BUFFER];
let range = ChunkRange { offset, length };
let response = blob::path_resolve(
self.scope.clone(),
normalize(path),
Some(range),
&mut req,
&mut rsp,
)
.map_err(|_| "BlobStore::get_range")?;
Ok(response.map(|response| response.blob))
}
pub fn delete(&self, path: &str) -> Result<()> {
let mut req = [0u8; REQUEST_BUFFER];
blob::blob_unlink(self.scope.clone(), normalize(path), &mut req)
.map_err(|_| "BlobStore::delete")
}
pub fn rename(&self, from: &str, to: &str) -> Result<()> {
let mut req = [0u8; REQUEST_BUFFER];
blob::blob_move(self.scope.clone(), normalize(from), normalize(to), &mut req)
.map_err(|_| "BlobStore::rename")
}
pub fn list(&self) -> Result<Vec<BlobPath>> {
let mut req = [0u8; REQUEST_BUFFER];
let mut rsp = crate::vec![0u8; 8192];
blob::paths_list(self.scope.clone(), None, &mut req, &mut rsp)
.map_err(|_| "BlobStore::list")
}
}
fn normalize(path: &str) -> String {
let trimmed = path.trim_start_matches('/');
let mut normalized = String::with_capacity(trimmed.len() + 1);
normalized.push('/');
normalized.push_str(trimmed);
normalized
}