1#![expect(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 _;
25
26use crate::backend;
27use crate::backend::BackendResult;
28use crate::backend::TreeEntriesNonRecursiveIterator;
29use crate::backend::TreeId;
30use crate::backend::TreeValue;
31use crate::matchers::Matcher;
32use crate::repo_path::RepoPath;
33use crate::repo_path::RepoPathBuf;
34use crate::repo_path::RepoPathComponent;
35use crate::store::Store;
36
37#[derive(Clone)]
38pub struct Tree {
39 store: Arc<Store>,
40 dir: RepoPathBuf,
41 id: TreeId,
42 data: Arc<backend::Tree>,
43}
44
45impl Debug for Tree {
46 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
47 f.debug_struct("Tree")
48 .field("dir", &self.dir)
49 .field("id", &self.id)
50 .finish()
51 }
52}
53
54impl PartialEq for Tree {
55 fn eq(&self, other: &Self) -> bool {
56 self.id == other.id && self.dir == other.dir
57 }
58}
59
60impl Eq for Tree {}
61
62impl Hash for Tree {
63 fn hash<H: Hasher>(&self, state: &mut H) {
64 self.dir.hash(state);
65 self.id.hash(state);
66 }
67}
68
69impl Tree {
70 pub fn new(store: Arc<Store>, dir: RepoPathBuf, id: TreeId, data: Arc<backend::Tree>) -> Self {
71 Self {
72 store,
73 dir,
74 id,
75 data,
76 }
77 }
78
79 pub fn empty(store: Arc<Store>, dir: RepoPathBuf) -> Self {
80 let id = store.empty_tree_id().clone();
81 Self {
82 store,
83 dir,
84 id,
85 data: Arc::new(backend::Tree::default()),
86 }
87 }
88
89 pub fn store(&self) -> &Arc<Store> {
90 &self.store
91 }
92
93 pub fn dir(&self) -> &RepoPath {
94 &self.dir
95 }
96
97 pub fn id(&self) -> &TreeId {
98 &self.id
99 }
100
101 pub fn data(&self) -> &backend::Tree {
102 &self.data
103 }
104
105 pub fn entries_non_recursive(&self) -> TreeEntriesNonRecursiveIterator<'_> {
106 self.data.entries()
107 }
108
109 pub fn entries_matching<'matcher>(
110 &self,
111 matcher: &'matcher dyn Matcher,
112 ) -> TreeEntriesIterator<'matcher> {
113 TreeEntriesIterator::new(self.clone(), matcher)
114 }
115
116 pub fn value(&self, basename: &RepoPathComponent) -> Option<&TreeValue> {
117 self.data.value(basename)
118 }
119
120 pub fn path_value(&self, path: &RepoPath) -> BackendResult<Option<TreeValue>> {
121 assert_eq!(self.dir(), RepoPath::root());
122 match path.split() {
123 Some((dir, basename)) => {
124 let tree = self.sub_tree_recursive(dir)?;
125 Ok(tree.and_then(|tree| tree.data.value(basename).cloned()))
126 }
127 None => Ok(Some(TreeValue::Tree(self.id.clone()))),
128 }
129 }
130
131 pub fn sub_tree(&self, name: &RepoPathComponent) -> BackendResult<Option<Self>> {
132 if let Some(sub_tree) = self.data.value(name) {
133 match sub_tree {
134 TreeValue::Tree(sub_tree_id) => {
135 let subdir = self.dir.join(name);
136 let sub_tree = self.store.get_tree(subdir, sub_tree_id)?;
137 Ok(Some(sub_tree))
138 }
139 _ => Ok(None),
140 }
141 } else {
142 Ok(None)
143 }
144 }
145
146 fn known_sub_tree(&self, subdir: RepoPathBuf, id: &TreeId) -> Self {
147 self.store.get_tree(subdir, id).unwrap()
148 }
149
150 pub fn sub_tree_recursive(&self, path: &RepoPath) -> BackendResult<Option<Self>> {
152 let mut current_tree = self.clone();
153 for name in path.components() {
154 match current_tree.sub_tree(name)? {
155 None => {
156 return Ok(None);
157 }
158 Some(sub_tree) => {
159 current_tree = sub_tree;
160 }
161 }
162 }
163 Ok(Some(current_tree))
167 }
168}
169
170pub struct TreeEntriesIterator<'matcher> {
171 stack: Vec<TreeEntriesDirItem>,
172 matcher: &'matcher dyn Matcher,
173}
174
175struct TreeEntriesDirItem {
176 tree: Tree,
177 entries: Vec<(RepoPathBuf, TreeValue)>,
178}
179
180impl From<Tree> for TreeEntriesDirItem {
181 fn from(tree: Tree) -> Self {
182 let mut entries = tree
183 .entries_non_recursive()
184 .map(|entry| (tree.dir().join(entry.name()), entry.value().clone()))
185 .collect_vec();
186 entries.reverse();
187 Self { tree, entries }
188 }
189}
190
191impl<'matcher> TreeEntriesIterator<'matcher> {
192 fn new(tree: Tree, matcher: &'matcher dyn Matcher) -> Self {
193 Self {
195 stack: vec![TreeEntriesDirItem::from(tree)],
196 matcher,
197 }
198 }
199}
200
201impl Iterator for TreeEntriesIterator<'_> {
202 type Item = (RepoPathBuf, TreeValue);
203
204 fn next(&mut self) -> Option<Self::Item> {
205 while let Some(top) = self.stack.last_mut() {
206 if let Some((path, value)) = top.entries.pop() {
207 match value {
208 TreeValue::Tree(id) => {
209 if self.matcher.visit(&path).is_nothing() {
211 continue;
212 }
213 let subtree = top.tree.known_sub_tree(path, &id);
214 self.stack.push(TreeEntriesDirItem::from(subtree));
215 }
216 value => {
217 if self.matcher.matches(&path) {
218 return Some((path, value));
219 }
220 }
221 };
222 } else {
223 self.stack.pop();
224 }
225 }
226 None
227 }
228}