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