use std::collections::HashMap;
use std::sync::Mutex;
use hm_plugin_protocol::ArchiveId;
use uuid::Uuid;
#[derive(Debug, Default)]
pub struct ArchiveStore {
archives: Mutex<HashMap<ArchiveId, Vec<u8>>>,
}
impl ArchiveStore {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn register(&self, bytes: Vec<u8>) -> ArchiveId {
let id = ArchiveId::from(Uuid::new_v4());
let _ = self.archives.lock().map(|mut m| m.insert(id, bytes));
id
}
#[must_use]
pub fn total_size(&self, id: ArchiveId) -> u64 {
self.archives
.lock()
.ok()
.and_then(|m| m.get(&id).map(|b| b.len() as u64))
.unwrap_or(0)
}
#[must_use]
pub fn get_bytes(&self, id: ArchiveId) -> Option<Vec<u8>> {
self.archives.lock().ok()?.get(&id).cloned()
}
#[must_use]
pub fn read(&self, id: ArchiveId, offset: u64, max: u64) -> Vec<u8> {
let Ok(g) = self.archives.lock() else {
return Vec::new();
};
let Some(bytes) = g.get(&id) else {
return Vec::new();
};
#[allow(
clippy::cast_possible_truncation,
reason = "archive sizes fit in usize on supported 64-bit hosts"
)]
let start = (offset as usize).min(bytes.len());
#[allow(
clippy::cast_possible_truncation,
reason = "archive sizes fit in usize on supported 64-bit hosts"
)]
let max_us = max as usize;
let end = start.saturating_add(max_us).min(bytes.len());
bytes[start..end].to_vec()
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
mod tests {
use super::*;
#[test]
fn register_then_read_round_trip() {
let s = ArchiveStore::new();
let id = s.register(b"hello world".to_vec());
assert_eq!(s.total_size(id), 11);
assert_eq!(s.read(id, 0, 5), b"hello");
assert_eq!(s.read(id, 6, 5), b"world");
assert_eq!(s.read(id, 100, 5), Vec::<u8>::new());
}
#[test]
fn unknown_id_returns_empty() {
let s = ArchiveStore::new();
let bogus = ArchiveId(Uuid::new_v4());
assert_eq!(s.total_size(bogus), 0);
assert_eq!(s.read(bogus, 0, 100), Vec::<u8>::new());
}
}