1#![allow(missing_docs)]
16
17use std::fmt::Debug;
18use std::fmt::Error;
19use std::fmt::Formatter;
20use std::hash::Hash;
21use std::hash::Hasher;
22use std::io::Read as _;
23use std::sync::Arc;
24
25use futures::future::try_join_all;
26use itertools::Itertools as _;
27use tracing::instrument;
28
29use crate::backend;
30use crate::backend::BackendError;
31use crate::backend::BackendResult;
32use crate::backend::ConflictId;
33use crate::backend::TreeEntriesNonRecursiveIterator;
34use crate::backend::TreeEntry;
35use crate::backend::TreeId;
36use crate::backend::TreeValue;
37use crate::files;
38use crate::files::MergeResult;
39use crate::matchers::EverythingMatcher;
40use crate::matchers::Matcher;
41use crate::merge::Merge;
42use crate::merge::MergedTreeVal;
43use crate::object_id::ObjectId as _;
44use crate::repo_path::RepoPath;
45use crate::repo_path::RepoPathBuf;
46use crate::repo_path::RepoPathComponent;
47use crate::store::Store;
48
49#[derive(Clone)]
50pub struct Tree {
51 store: Arc<Store>,
52 dir: RepoPathBuf,
53 id: TreeId,
54 data: Arc<backend::Tree>,
55}
56
57impl Debug for Tree {
58 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
59 f.debug_struct("Tree")
60 .field("dir", &self.dir)
61 .field("id", &self.id)
62 .finish()
63 }
64}
65
66impl PartialEq for Tree {
67 fn eq(&self, other: &Self) -> bool {
68 self.id == other.id && self.dir == other.dir
69 }
70}
71
72impl Eq for Tree {}
73
74impl Hash for Tree {
75 fn hash<H: Hasher>(&self, state: &mut H) {
76 self.dir.hash(state);
77 self.id.hash(state);
78 }
79}
80
81impl Tree {
82 pub fn new(store: Arc<Store>, dir: RepoPathBuf, id: TreeId, data: Arc<backend::Tree>) -> Self {
83 Tree {
84 store,
85 dir,
86 id,
87 data,
88 }
89 }
90
91 pub fn empty(store: Arc<Store>, dir: RepoPathBuf) -> Self {
92 let id = store.empty_tree_id().clone();
93 Tree {
94 store,
95 dir,
96 id,
97 data: Arc::new(backend::Tree::default()),
98 }
99 }
100
101 pub fn store(&self) -> &Arc<Store> {
102 &self.store
103 }
104
105 pub fn dir(&self) -> &RepoPath {
106 &self.dir
107 }
108
109 pub fn id(&self) -> &TreeId {
110 &self.id
111 }
112
113 pub fn data(&self) -> &backend::Tree {
114 &self.data
115 }
116
117 pub fn entries_non_recursive(&self) -> TreeEntriesNonRecursiveIterator {
118 self.data.entries()
119 }
120
121 pub fn entries(&self) -> TreeEntriesIterator<'static> {
122 TreeEntriesIterator::new(self.clone(), &EverythingMatcher)
123 }
124
125 pub fn entries_matching<'matcher>(
126 &self,
127 matcher: &'matcher dyn Matcher,
128 ) -> TreeEntriesIterator<'matcher> {
129 TreeEntriesIterator::new(self.clone(), matcher)
130 }
131
132 pub fn entry(&self, basename: &RepoPathComponent) -> Option<TreeEntry> {
133 self.data.entry(basename)
134 }
135
136 pub fn value(&self, basename: &RepoPathComponent) -> Option<&TreeValue> {
137 self.data.value(basename)
138 }
139
140 pub fn path_value(&self, path: &RepoPath) -> BackendResult<Option<TreeValue>> {
141 assert_eq!(self.dir(), RepoPath::root());
142 match path.split() {
143 Some((dir, basename)) => {
144 let tree = self.sub_tree_recursive(dir)?;
145 Ok(tree.and_then(|tree| tree.data.value(basename).cloned()))
146 }
147 None => Ok(Some(TreeValue::Tree(self.id.clone()))),
148 }
149 }
150
151 pub fn sub_tree(&self, name: &RepoPathComponent) -> BackendResult<Option<Tree>> {
152 if let Some(sub_tree) = self.data.value(name) {
153 match sub_tree {
154 TreeValue::Tree(sub_tree_id) => {
155 let subdir = self.dir.join(name);
156 let sub_tree = self.store.get_tree(subdir, sub_tree_id)?;
157 Ok(Some(sub_tree))
158 }
159 _ => Ok(None),
160 }
161 } else {
162 Ok(None)
163 }
164 }
165
166 fn known_sub_tree(&self, subdir: RepoPathBuf, id: &TreeId) -> Tree {
167 self.store.get_tree(subdir, id).unwrap()
168 }
169
170 pub fn sub_tree_recursive(&self, path: &RepoPath) -> BackendResult<Option<Tree>> {
172 let mut current_tree = self.clone();
173 for name in path.components() {
174 match current_tree.sub_tree(name)? {
175 None => {
176 return Ok(None);
177 }
178 Some(sub_tree) => {
179 current_tree = sub_tree;
180 }
181 }
182 }
183 Ok(Some(current_tree))
187 }
188
189 pub fn conflicts_matching(&self, matcher: &dyn Matcher) -> Vec<(RepoPathBuf, ConflictId)> {
190 let mut conflicts = vec![];
191 for (name, value) in self.entries_matching(matcher) {
192 if let TreeValue::Conflict(id) = value {
193 conflicts.push((name.clone(), id.clone()));
194 }
195 }
196 conflicts
197 }
198
199 #[instrument]
200 pub fn conflicts(&self) -> Vec<(RepoPathBuf, ConflictId)> {
201 self.conflicts_matching(&EverythingMatcher)
202 }
203
204 pub fn has_conflict(&self) -> bool {
205 !self.conflicts().is_empty()
206 }
207}
208
209pub struct TreeEntriesIterator<'matcher> {
210 stack: Vec<TreeEntriesDirItem>,
211 matcher: &'matcher dyn Matcher,
212}
213
214struct TreeEntriesDirItem {
215 tree: Tree,
216 entries: Vec<(RepoPathBuf, TreeValue)>,
217}
218
219impl From<Tree> for TreeEntriesDirItem {
220 fn from(tree: Tree) -> Self {
221 let mut entries = tree
222 .entries_non_recursive()
223 .map(|entry| (tree.dir().join(entry.name()), entry.value().clone()))
224 .collect_vec();
225 entries.reverse();
226 Self { tree, entries }
227 }
228}
229
230impl<'matcher> TreeEntriesIterator<'matcher> {
231 fn new(tree: Tree, matcher: &'matcher dyn Matcher) -> Self {
232 Self {
234 stack: vec![TreeEntriesDirItem::from(tree)],
235 matcher,
236 }
237 }
238}
239
240impl Iterator for TreeEntriesIterator<'_> {
241 type Item = (RepoPathBuf, TreeValue);
242
243 fn next(&mut self) -> Option<Self::Item> {
244 while let Some(top) = self.stack.last_mut() {
245 if let Some((path, value)) = top.entries.pop() {
246 match value {
247 TreeValue::Tree(id) => {
248 if self.matcher.visit(&path).is_nothing() {
250 continue;
251 }
252 let subtree = top.tree.known_sub_tree(path, &id);
253 self.stack.push(TreeEntriesDirItem::from(subtree));
254 }
255 value => {
256 if self.matcher.matches(&path) {
257 return Some((path, value));
258 }
259 }
260 };
261 } else {
262 self.stack.pop();
263 }
264 }
265 None
266 }
267}
268
269pub async fn try_resolve_file_conflict(
274 store: &Store,
275 filename: &RepoPath,
276 conflict: &MergedTreeVal<'_>,
277) -> BackendResult<Option<TreeValue>> {
278 let Some(file_id_conflict) = conflict.maybe_map(|term| match term {
283 Some(TreeValue::File { id, executable: _ }) => Some(id),
284 _ => None,
285 }) else {
286 return Ok(None);
287 };
288 let Some(executable_conflict) = conflict.maybe_map(|term| match term {
289 Some(TreeValue::File { id: _, executable }) => Some(executable),
290 _ => None,
291 }) else {
292 return Ok(None);
293 };
294 let Some(&&executable) = executable_conflict.resolve_trivial() else {
295 return Ok(None);
297 };
298 if let Some(&resolved_file_id) = file_id_conflict.resolve_trivial() {
299 return Ok(Some(TreeValue::File {
302 id: resolved_file_id.clone(),
303 executable,
304 }));
305 }
306
307 let file_id_conflict = file_id_conflict.simplify();
314
315 let content_futures = file_id_conflict.into_iter().map(|file_id| async {
316 let mut content = vec![];
317 let mut reader = store.read_file_async(filename, file_id).await?;
318 reader
319 .read_to_end(&mut content)
320 .map_err(|err| BackendError::ReadObject {
321 object_type: file_id.object_type(),
322 hash: file_id.hex(),
323 source: err.into(),
324 })?;
325 BackendResult::Ok(content)
326 });
327 let contents = Merge::from_vec(try_join_all(content_futures).await?);
328 let merge_result = files::merge(&contents);
329 match merge_result {
330 MergeResult::Resolved(merged_content) => {
331 let id = store
332 .write_file(filename, &mut merged_content.as_slice())
333 .await?;
334 Ok(Some(TreeValue::File { id, executable }))
335 }
336 MergeResult::Conflict(_) => Ok(None),
337 }
338}