1#![allow(missing_docs)]
16
17use std::collections::BTreeMap;
18use std::sync::Arc;
19
20use pollster::FutureExt as _;
21
22use crate::backend;
23use crate::backend::BackendResult;
24use crate::backend::TreeId;
25use crate::backend::TreeValue;
26use crate::repo_path::RepoPath;
27use crate::repo_path::RepoPathBuf;
28use crate::store::Store;
29use crate::tree::Tree;
30
31#[derive(Debug)]
32enum Override {
33 Tombstone,
34 Replace(TreeValue),
35}
36
37#[derive(Debug)]
38pub struct TreeBuilder {
39 store: Arc<Store>,
40 base_tree_id: TreeId,
41 overrides: BTreeMap<RepoPathBuf, Override>,
42}
43
44impl TreeBuilder {
45 pub fn new(store: Arc<Store>, base_tree_id: TreeId) -> TreeBuilder {
46 let overrides = BTreeMap::new();
47 TreeBuilder {
48 store,
49 base_tree_id,
50 overrides,
51 }
52 }
53
54 pub fn store(&self) -> &Store {
55 self.store.as_ref()
56 }
57
58 pub fn set(&mut self, path: RepoPathBuf, value: TreeValue) {
59 assert!(!path.is_root());
60 self.overrides.insert(path, Override::Replace(value));
61 }
62
63 pub fn remove(&mut self, path: RepoPathBuf) {
64 assert!(!path.is_root());
65 self.overrides.insert(path, Override::Tombstone);
66 }
67
68 pub fn set_or_remove(&mut self, path: RepoPathBuf, value: Option<TreeValue>) {
69 assert!(!path.is_root());
70 if let Some(value) = value {
71 self.overrides.insert(path, Override::Replace(value));
72 } else {
73 self.overrides.insert(path, Override::Tombstone);
74 }
75 }
76
77 pub fn write_tree(self) -> BackendResult<TreeId> {
78 if self.overrides.is_empty() {
79 return Ok(self.base_tree_id);
80 }
81
82 let mut trees_to_write = self.get_base_trees()?;
83
84 for (path, file_override) in self.overrides {
86 let (dir, basename) = path.split().unwrap();
87 let tree = trees_to_write.get_mut(dir).unwrap();
88 match file_override {
89 Override::Replace(value) => {
90 tree.set(basename.to_owned(), value);
91 }
92 Override::Tombstone => {
93 tree.remove(basename);
94 }
95 }
96 }
97
98 let store = &self.store;
102 while let Some((dir, tree)) = trees_to_write.pop_last() {
103 if let Some((parent, basename)) = dir.split() {
104 let parent_tree = trees_to_write.get_mut(parent).unwrap();
105 if tree.is_empty() {
106 if let Some(TreeValue::Tree(_)) = parent_tree.value(basename) {
107 parent_tree.remove(basename);
108 } else {
109 }
111 } else {
112 let tree = store.write_tree(&dir, tree).block_on()?;
113 parent_tree.set(basename.to_owned(), TreeValue::Tree(tree.id().clone()));
114 }
115 } else {
116 assert!(trees_to_write.is_empty());
118 let written_tree = store.write_tree(&dir, tree).block_on()?;
119 return Ok(written_tree.id().clone());
120 }
121 }
122
123 unreachable!("trees_to_write must contain the root tree");
124 }
125
126 fn get_base_trees(&self) -> BackendResult<BTreeMap<RepoPathBuf, backend::Tree>> {
127 let store = &self.store;
128 let mut tree_cache = {
129 let dir = RepoPathBuf::root();
130 let tree = store.get_tree(dir.clone(), &self.base_tree_id)?;
131 BTreeMap::from([(dir, tree)])
132 };
133
134 fn populate_trees<'a>(
135 tree_cache: &'a mut BTreeMap<RepoPathBuf, Tree>,
136 store: &Arc<Store>,
137 dir: &RepoPath,
138 ) -> BackendResult<&'a Tree> {
139 if tree_cache.contains_key(dir) {
141 return Ok(tree_cache.get(dir).unwrap());
142 }
143 let (parent, basename) = dir.split().expect("root must be populated");
144 let tree = populate_trees(tree_cache, store, parent)?
145 .sub_tree(basename)?
146 .unwrap_or_else(|| Tree::empty(store.clone(), dir.to_owned()));
147 Ok(tree_cache.entry(dir.to_owned()).or_insert(tree))
148 }
149
150 for path in self.overrides.keys() {
151 let parent = path.parent().unwrap();
152 populate_trees(&mut tree_cache, store, parent)?;
153 }
154
155 Ok(tree_cache
156 .into_iter()
157 .map(|(dir, tree)| (dir, tree.data().clone()))
158 .collect())
159 }
160}