1use crate::{GitObject, ObjectId, Result};
7use std::sync::Arc;
8
9pub trait ObjectStoreBackend: Send + Sync {
13 fn put(&self, object: GitObject) -> Result<ObjectId>;
15
16 fn get(&self, id: &ObjectId) -> Result<Option<GitObject>>;
18
19 fn contains(&self, id: &ObjectId) -> Result<bool>;
21
22 fn delete(&self, id: &ObjectId) -> Result<bool>;
24
25 fn len(&self) -> Result<usize>;
27
28 fn is_empty(&self) -> Result<bool> {
30 Ok(self.len()? == 0)
31 }
32
33 fn list_objects(&self) -> Result<Vec<ObjectId>>;
35
36 fn batch_put(&self, objects: Vec<GitObject>) -> Result<Vec<ObjectId>> {
38 objects.into_iter().map(|obj| self.put(obj)).collect()
39 }
40
41 fn batch_get(&self, ids: &[ObjectId]) -> Result<Vec<Option<GitObject>>> {
43 ids.iter().map(|id| self.get(id)).collect()
44 }
45
46 fn flush(&self) -> Result<()> {
48 Ok(())
49 }
50
51 fn compact(&self) -> Result<()> {
53 Ok(())
54 }
55}
56
57impl<T: ObjectStoreBackend> ObjectStoreBackend for Arc<T> {
59 fn put(&self, object: GitObject) -> Result<ObjectId> {
60 (**self).put(object)
61 }
62
63 fn get(&self, id: &ObjectId) -> Result<Option<GitObject>> {
64 (**self).get(id)
65 }
66
67 fn contains(&self, id: &ObjectId) -> Result<bool> {
68 (**self).contains(id)
69 }
70
71 fn delete(&self, id: &ObjectId) -> Result<bool> {
72 (**self).delete(id)
73 }
74
75 fn len(&self) -> Result<usize> {
76 (**self).len()
77 }
78
79 fn list_objects(&self) -> Result<Vec<ObjectId>> {
80 (**self).list_objects()
81 }
82
83 fn batch_put(&self, objects: Vec<GitObject>) -> Result<Vec<ObjectId>> {
84 (**self).batch_put(objects)
85 }
86
87 fn batch_get(&self, ids: &[ObjectId]) -> Result<Vec<Option<GitObject>>> {
88 (**self).batch_get(ids)
89 }
90
91 fn flush(&self) -> Result<()> {
92 (**self).flush()
93 }
94
95 fn compact(&self) -> Result<()> {
96 (**self).compact()
97 }
98}
99
100pub trait StorageBackend: ObjectStoreBackend {
102 fn open(path: &std::path::Path) -> Result<Self>
104 where
105 Self: Sized;
106
107 fn close(&self) -> Result<()> {
109 self.flush()
110 }
111
112 fn stats(&self) -> StorageStats {
114 StorageStats::default()
115 }
116}
117
118#[derive(Debug, Clone, Default)]
120pub struct StorageStats {
121 pub object_count: u64,
123 pub total_size_bytes: u64,
125 pub disk_size_bytes: Option<u64>,
127 pub reads: u64,
129 pub writes: u64,
131 pub cache_hit_ratio: Option<f64>,
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138
139 struct MockStorage;
141
142 impl ObjectStoreBackend for MockStorage {
143 fn put(&self, _object: GitObject) -> Result<ObjectId> {
144 Ok(ObjectId::from_bytes([0u8; 20]))
145 }
146
147 fn get(&self, _id: &ObjectId) -> Result<Option<GitObject>> {
148 Ok(None)
149 }
150
151 fn contains(&self, _id: &ObjectId) -> Result<bool> {
152 Ok(false)
153 }
154
155 fn delete(&self, _id: &ObjectId) -> Result<bool> {
156 Ok(false)
157 }
158
159 fn len(&self) -> Result<usize> {
160 Ok(0)
161 }
162
163 fn list_objects(&self) -> Result<Vec<ObjectId>> {
164 Ok(vec![])
165 }
166 }
167
168 #[test]
169 fn test_is_empty_default() {
170 let storage = MockStorage;
171 assert!(storage.is_empty().unwrap());
172 }
173}