jujutsu_lib/
store.rs

1// Copyright 2020 The Jujutsu Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use 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/// Wraps the low-level backend and makes it return more convenient types. Also
29/// adds the root commit and adds caching.
30#[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        // TODO: This should also do caching like write_commit does.
126        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}