1#![expect(missing_docs)]
16
17use std::fmt::Debug;
18use std::fmt::Formatter;
19use std::num::NonZeroUsize;
20use std::pin::Pin;
21use std::sync::Arc;
22use std::sync::Mutex;
23use std::time::SystemTime;
24
25use clru::CLruCache;
26use futures::AsyncRead;
27use futures::stream::BoxStream;
28use pollster::FutureExt as _;
29
30use crate::backend;
31use crate::backend::Backend;
32use crate::backend::BackendResult;
33use crate::backend::ChangeId;
34use crate::backend::CommitId;
35use crate::backend::CopyRecord;
36use crate::backend::FileId;
37use crate::backend::SigningFn;
38use crate::backend::SymlinkId;
39use crate::backend::TreeId;
40use crate::commit::Commit;
41use crate::index::Index;
42use crate::merge::Merge;
43use crate::merged_tree::MergedTree;
44use crate::repo_path::RepoPath;
45use crate::repo_path::RepoPathBuf;
46use crate::signing::Signer;
47use crate::tree::Tree;
48use crate::tree_merge::MergeOptions;
49
50pub(crate) const COMMIT_CACHE_CAPACITY: NonZeroUsize = NonZeroUsize::new(100).unwrap();
53const TREE_CACHE_CAPACITY: NonZeroUsize = NonZeroUsize::new(1000).unwrap();
54
55pub struct Store {
58 backend: Box<dyn Backend>,
59 signer: Signer,
60 commit_cache: Mutex<CLruCache<CommitId, Arc<backend::Commit>>>,
61 tree_cache: Mutex<CLruCache<(RepoPathBuf, TreeId), Arc<backend::Tree>>>,
62 merge_options: MergeOptions,
63}
64
65impl Debug for Store {
66 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
67 f.debug_struct("Store")
68 .field("backend", &self.backend)
69 .finish_non_exhaustive()
70 }
71}
72
73impl Store {
74 pub fn new(
75 backend: Box<dyn Backend>,
76 signer: Signer,
77 merge_options: MergeOptions,
78 ) -> Arc<Self> {
79 Arc::new(Self {
80 backend,
81 signer,
82 commit_cache: Mutex::new(CLruCache::new(COMMIT_CACHE_CAPACITY)),
83 tree_cache: Mutex::new(CLruCache::new(TREE_CACHE_CAPACITY)),
84 merge_options,
85 })
86 }
87
88 pub fn backend(&self) -> &dyn Backend {
89 self.backend.as_ref()
90 }
91
92 pub fn backend_impl<T: Backend>(&self) -> Option<&T> {
94 self.backend.downcast_ref()
95 }
96
97 pub fn signer(&self) -> &Signer {
98 &self.signer
99 }
100
101 pub fn merge_options(&self) -> &MergeOptions {
103 &self.merge_options
104 }
105
106 pub fn get_copy_records(
107 &self,
108 paths: Option<&[RepoPathBuf]>,
109 root: &CommitId,
110 head: &CommitId,
111 ) -> BackendResult<BoxStream<'_, BackendResult<CopyRecord>>> {
112 self.backend.get_copy_records(paths, root, head)
113 }
114
115 pub fn commit_id_length(&self) -> usize {
116 self.backend.commit_id_length()
117 }
118
119 pub fn change_id_length(&self) -> usize {
120 self.backend.change_id_length()
121 }
122
123 pub fn root_commit_id(&self) -> &CommitId {
124 self.backend.root_commit_id()
125 }
126
127 pub fn root_change_id(&self) -> &ChangeId {
128 self.backend.root_change_id()
129 }
130
131 pub fn empty_tree_id(&self) -> &TreeId {
132 self.backend.empty_tree_id()
133 }
134
135 pub fn concurrency(&self) -> usize {
136 self.backend.concurrency()
137 }
138
139 pub fn empty_merged_tree(self: &Arc<Self>) -> MergedTree {
140 let empty_tree_id = self.backend.empty_tree_id().clone();
141 MergedTree::resolved(self.clone(), empty_tree_id)
142 }
143
144 pub fn empty_merged_tree_id(&self) -> Merge<TreeId> {
145 Merge::resolved(self.backend.empty_tree_id().clone())
146 }
147
148 pub fn root_commit(self: &Arc<Self>) -> Commit {
149 self.get_commit(self.backend.root_commit_id()).unwrap()
150 }
151
152 pub fn get_commit(self: &Arc<Self>, id: &CommitId) -> BackendResult<Commit> {
153 self.get_commit_async(id).block_on()
154 }
155
156 pub async fn get_commit_async(self: &Arc<Self>, id: &CommitId) -> BackendResult<Commit> {
157 let data = self.get_backend_commit(id).await?;
158 Ok(Commit::new(self.clone(), id.clone(), data))
159 }
160
161 async fn get_backend_commit(&self, id: &CommitId) -> BackendResult<Arc<backend::Commit>> {
162 {
163 let mut locked_cache = self.commit_cache.lock().unwrap();
164 if let Some(data) = locked_cache.get(id).cloned() {
165 return Ok(data);
166 }
167 }
168 let commit = self.backend.read_commit(id).await?;
169 let data = Arc::new(commit);
170 let mut locked_cache = self.commit_cache.lock().unwrap();
171 locked_cache.put(id.clone(), data.clone());
172 Ok(data)
173 }
174
175 pub async fn write_commit(
176 self: &Arc<Self>,
177 commit: backend::Commit,
178 sign_with: Option<&mut SigningFn<'_>>,
179 ) -> BackendResult<Commit> {
180 assert!(!commit.parents.is_empty());
181
182 let (commit_id, commit) = self.backend.write_commit(commit, sign_with).await?;
183 let data = Arc::new(commit);
184 {
185 let mut locked_cache = self.commit_cache.lock().unwrap();
186 locked_cache.put(commit_id.clone(), data.clone());
187 }
188
189 Ok(Commit::new(self.clone(), commit_id, data))
190 }
191
192 pub async fn get_tree(self: &Arc<Self>, dir: RepoPathBuf, id: &TreeId) -> BackendResult<Tree> {
193 let data = self.get_backend_tree(&dir, id).await?;
194 Ok(Tree::new(self.clone(), dir, id.clone(), data))
195 }
196
197 async fn get_backend_tree(
198 &self,
199 dir: &RepoPath,
200 id: &TreeId,
201 ) -> BackendResult<Arc<backend::Tree>> {
202 let key = (dir.to_owned(), id.clone());
203 {
204 let mut locked_cache = self.tree_cache.lock().unwrap();
205 if let Some(data) = locked_cache.get(&key).cloned() {
206 return Ok(data);
207 }
208 }
209 let data = self.backend.read_tree(dir, id).await?;
210 let data = Arc::new(data);
211 let mut locked_cache = self.tree_cache.lock().unwrap();
212 locked_cache.put(key, data.clone());
213 Ok(data)
214 }
215
216 pub async fn write_tree(
217 self: &Arc<Self>,
218 path: &RepoPath,
219 tree: backend::Tree,
220 ) -> BackendResult<Tree> {
221 let tree_id = self.backend.write_tree(path, &tree).await?;
222 let data = Arc::new(tree);
223 {
224 let mut locked_cache = self.tree_cache.lock().unwrap();
225 locked_cache.put((path.to_owned(), tree_id.clone()), data.clone());
226 }
227
228 Ok(Tree::new(self.clone(), path.to_owned(), tree_id, data))
229 }
230
231 pub async fn read_file(
232 &self,
233 path: &RepoPath,
234 id: &FileId,
235 ) -> BackendResult<Pin<Box<dyn AsyncRead + Send>>> {
236 self.backend.read_file(path, id).await
237 }
238
239 pub async fn write_file(
240 &self,
241 path: &RepoPath,
242 contents: &mut (dyn AsyncRead + Send + Unpin),
243 ) -> BackendResult<FileId> {
244 self.backend.write_file(path, contents).await
245 }
246
247 pub async fn read_symlink(&self, path: &RepoPath, id: &SymlinkId) -> BackendResult<String> {
248 self.backend.read_symlink(path, id).await
249 }
250
251 pub async fn write_symlink(&self, path: &RepoPath, contents: &str) -> BackendResult<SymlinkId> {
252 self.backend.write_symlink(path, contents).await
253 }
254
255 pub fn gc(&self, index: &dyn Index, keep_newer: SystemTime) -> BackendResult<()> {
256 self.backend.gc(index, keep_newer)
257 }
258
259 pub fn clear_caches(&self) {
261 self.commit_cache.lock().unwrap().clear();
262 self.tree_cache.lock().unwrap().clear();
263 }
264}