1use std::collections::HashMap;
16use std::io::Read;
17use std::sync::{Arc, RwLock};
18
19use crate::backend;
20use crate::backend::{
21 Backend, BackendResult, ChangeId, CommitId, Conflict, ConflictId, FileId, SymlinkId, TreeId,
22};
23use crate::commit::Commit;
24use crate::repo_path::RepoPath;
25use crate::tree::Tree;
26use crate::tree_builder::TreeBuilder;
27
28#[derive(Debug)]
31pub struct Store {
32 backend: Box<dyn Backend>,
33 commit_cache: RwLock<HashMap<CommitId, Arc<backend::Commit>>>,
34 tree_cache: RwLock<HashMap<(RepoPath, TreeId), Arc<backend::Tree>>>,
35}
36
37impl Store {
38 pub fn new(backend: Box<dyn Backend>) -> Arc<Self> {
39 Arc::new(Store {
40 backend,
41 commit_cache: Default::default(),
42 tree_cache: Default::default(),
43 })
44 }
45
46 pub fn commit_id_length(&self) -> usize {
47 self.backend.commit_id_length()
48 }
49
50 pub fn change_id_length(&self) -> usize {
51 self.backend.change_id_length()
52 }
53
54 pub fn git_repo(&self) -> Option<git2::Repository> {
55 self.backend.git_repo()
56 }
57
58 pub fn empty_tree_id(&self) -> &TreeId {
59 self.backend.empty_tree_id()
60 }
61
62 pub fn root_commit_id(&self) -> &CommitId {
63 self.backend.root_commit_id()
64 }
65
66 pub fn root_change_id(&self) -> &ChangeId {
67 self.backend.root_change_id()
68 }
69
70 pub fn root_commit(self: &Arc<Self>) -> Commit {
71 self.get_commit(self.backend.root_commit_id()).unwrap()
72 }
73
74 pub fn get_commit(self: &Arc<Self>, id: &CommitId) -> BackendResult<Commit> {
75 let data = self.get_backend_commit(id)?;
76 Ok(Commit::new(self.clone(), id.clone(), data))
77 }
78
79 fn get_backend_commit(&self, id: &CommitId) -> BackendResult<Arc<backend::Commit>> {
80 {
81 let read_locked_cached = self.commit_cache.read().unwrap();
82 if let Some(data) = read_locked_cached.get(id).cloned() {
83 return Ok(data);
84 }
85 }
86 let commit = self.backend.read_commit(id)?;
87 let data = Arc::new(commit);
88 let mut write_locked_cache = self.commit_cache.write().unwrap();
89 write_locked_cache.insert(id.clone(), data.clone());
90 Ok(data)
91 }
92
93 pub fn write_commit(self: &Arc<Self>, commit: backend::Commit) -> BackendResult<Commit> {
94 assert!(!commit.parents.is_empty());
95 let commit_id = self.backend.write_commit(&commit)?;
96 let data = Arc::new(commit);
97 {
98 let mut write_locked_cache = self.commit_cache.write().unwrap();
99 write_locked_cache.insert(commit_id.clone(), data.clone());
100 }
101
102 Ok(Commit::new(self.clone(), commit_id, data))
103 }
104
105 pub fn get_tree(self: &Arc<Self>, dir: &RepoPath, id: &TreeId) -> BackendResult<Tree> {
106 let data = self.get_backend_tree(dir, id)?;
107 Ok(Tree::new(self.clone(), dir.clone(), id.clone(), data))
108 }
109
110 fn get_backend_tree(&self, dir: &RepoPath, id: &TreeId) -> BackendResult<Arc<backend::Tree>> {
111 let key = (dir.clone(), id.clone());
112 {
113 let read_locked_cache = self.tree_cache.read().unwrap();
114 if let Some(data) = read_locked_cache.get(&key).cloned() {
115 return Ok(data);
116 }
117 }
118 let data = Arc::new(self.backend.read_tree(dir, id)?);
119 let mut write_locked_cache = self.tree_cache.write().unwrap();
120 write_locked_cache.insert(key, data.clone());
121 Ok(data)
122 }
123
124 pub fn write_tree(&self, path: &RepoPath, contents: &backend::Tree) -> BackendResult<TreeId> {
125 self.backend.write_tree(path, contents)
127 }
128
129 pub fn read_file(&self, path: &RepoPath, id: &FileId) -> BackendResult<Box<dyn Read>> {
130 self.backend.read_file(path, id)
131 }
132
133 pub fn write_file(&self, path: &RepoPath, contents: &mut dyn Read) -> BackendResult<FileId> {
134 self.backend.write_file(path, contents)
135 }
136
137 pub fn read_symlink(&self, path: &RepoPath, id: &SymlinkId) -> BackendResult<String> {
138 self.backend.read_symlink(path, id)
139 }
140
141 pub fn write_symlink(&self, path: &RepoPath, contents: &str) -> BackendResult<SymlinkId> {
142 self.backend.write_symlink(path, contents)
143 }
144
145 pub fn read_conflict(&self, path: &RepoPath, id: &ConflictId) -> BackendResult<Conflict> {
146 self.backend.read_conflict(path, id)
147 }
148
149 pub fn write_conflict(
150 &self,
151 path: &RepoPath,
152 contents: &Conflict,
153 ) -> BackendResult<ConflictId> {
154 self.backend.write_conflict(path, contents)
155 }
156
157 pub fn tree_builder(self: &Arc<Self>, base_tree_id: TreeId) -> TreeBuilder {
158 TreeBuilder::new(self.clone(), base_tree_id)
159 }
160}