harmont_cli/orchestrator/
archive.rs1use std::collections::HashMap;
11use std::sync::Mutex;
12
13use hm_plugin_protocol::ArchiveId;
14use uuid::Uuid;
15
16#[derive(Debug, Default)]
17pub struct ArchiveStore {
18 archives: Mutex<HashMap<ArchiveId, Vec<u8>>>,
19}
20
21impl ArchiveStore {
22 #[must_use]
23 pub fn new() -> Self {
24 Self::default()
25 }
26
27 pub fn register(&self, bytes: Vec<u8>) -> ArchiveId {
29 let id = ArchiveId(Uuid::new_v4());
30 let _ = self.archives.lock().map(|mut m| m.insert(id, bytes));
31 id
32 }
33
34 #[must_use]
37 pub fn total_size(&self, id: ArchiveId) -> u64 {
38 self.archives
39 .lock()
40 .ok()
41 .and_then(|m| m.get(&id).map(|b| b.len() as u64))
42 .unwrap_or(0)
43 }
44
45 #[must_use]
48 pub fn read(&self, id: ArchiveId, offset: u64, max: u64) -> Vec<u8> {
49 let Ok(g) = self.archives.lock() else {
53 return Vec::new();
54 };
55 let Some(bytes) = g.get(&id) else {
56 return Vec::new();
57 };
58 #[allow(
62 clippy::cast_possible_truncation,
63 reason = "archive sizes fit in usize on supported 64-bit hosts"
64 )]
65 let start = (offset as usize).min(bytes.len());
66 #[allow(
67 clippy::cast_possible_truncation,
68 reason = "archive sizes fit in usize on supported 64-bit hosts"
69 )]
70 let max_us = max as usize;
71 let end = start.saturating_add(max_us).min(bytes.len());
72 bytes[start..end].to_vec()
73 }
74}
75
76#[cfg(test)]
77#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
78mod tests {
79 use super::*;
80
81 #[test]
82 fn register_then_read_round_trip() {
83 let s = ArchiveStore::new();
84 let id = s.register(b"hello world".to_vec());
85 assert_eq!(s.total_size(id), 11);
86 assert_eq!(s.read(id, 0, 5), b"hello");
87 assert_eq!(s.read(id, 6, 5), b"world");
88 assert_eq!(s.read(id, 100, 5), Vec::<u8>::new());
89 }
90
91 #[test]
92 fn unknown_id_returns_empty() {
93 let s = ArchiveStore::new();
94 let bogus = ArchiveId(Uuid::new_v4());
95 assert_eq!(s.total_size(bogus), 0);
96 assert_eq!(s.read(bogus, 0, 100), Vec::<u8>::new());
97 }
98}