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