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