jujube_lib/
tree_builder.rs1use std::collections::{BTreeMap, HashSet};
16
17use crate::repo_path::{DirRepoPath, RepoPath, RepoPathJoin};
18use crate::store;
19use crate::store::{TreeId, TreeValue};
20use crate::store_wrapper::StoreWrapper;
21use crate::tree::Tree;
22use std::sync::Arc;
23
24#[derive(Debug)]
25enum Override {
26 Tombstone,
27 Replace(TreeValue),
28}
29
30#[derive(Debug)]
31pub struct TreeBuilder {
32 store: Arc<StoreWrapper>,
33 base_tree_id: TreeId,
34 overrides: BTreeMap<RepoPath, Override>,
35}
36
37impl TreeBuilder {
38 pub fn new(store: Arc<StoreWrapper>, base_tree_id: TreeId) -> TreeBuilder {
39 let overrides = BTreeMap::new();
40 TreeBuilder {
41 store,
42 base_tree_id,
43 overrides,
44 }
45 }
46
47 pub fn repo(&self) -> &StoreWrapper {
48 self.store.as_ref()
49 }
50
51 pub fn set(&mut self, path: RepoPath, value: TreeValue) {
52 self.overrides.insert(path, Override::Replace(value));
53 }
54
55 pub fn remove(&mut self, path: RepoPath) {
56 self.overrides.insert(path, Override::Tombstone);
57 }
58
59 pub fn write_tree(mut self) -> TreeId {
60 let mut trees_to_write = self.get_base_trees();
61 if trees_to_write.is_empty() {
62 return self.base_tree_id;
63 }
64
65 for (path, file_override) in self.overrides {
67 if let Some((dir, basename)) = path.split() {
68 let tree = trees_to_write.get_mut(dir).unwrap();
69 match file_override {
70 Override::Replace(value) => {
71 tree.set(basename.value().to_string(), value);
72 }
73 Override::Tombstone => {
74 tree.remove(basename.value());
75 }
76 }
77 }
78 }
79
80 let store = self.store.as_ref();
82 loop {
83 let mut dirs_to_write: HashSet<DirRepoPath> =
84 trees_to_write.keys().cloned().into_iter().collect();
85
86 for dir in trees_to_write.keys() {
87 if let Some(parent) = dir.parent() {
88 dirs_to_write.remove(&parent);
89 }
90 }
91
92 for dir in dirs_to_write {
93 let tree = trees_to_write.remove(&dir).unwrap();
94
95 if let Some((parent, basename)) = dir.split() {
96 let parent_tree = trees_to_write.get_mut(&parent).unwrap();
97 if tree.is_empty() {
98 parent_tree.remove(basename.value());
99 } else {
100 let tree_id = store.write_tree(&dir, &tree).unwrap();
101 parent_tree.set(basename.value().to_string(), TreeValue::Tree(tree_id));
102 }
103 } else {
104 return store.write_tree(&dir, &tree).unwrap();
106 }
107 }
108 }
109 }
110
111 fn get_base_trees(&mut self) -> BTreeMap<DirRepoPath, store::Tree> {
112 let mut tree_cache = BTreeMap::new();
113 let mut base_trees = BTreeMap::new();
114 let store = self.store.clone();
115
116 let mut populate_trees = |dir: &DirRepoPath| {
117 let mut current_dir = DirRepoPath::root();
118
119 if !tree_cache.contains_key(¤t_dir) {
120 let tree = store.get_tree(¤t_dir, &self.base_tree_id).unwrap();
121 let store_tree = tree.data().clone();
122 tree_cache.insert(current_dir.clone(), tree);
123 base_trees.insert(current_dir.clone(), store_tree);
124 }
125
126 for component in dir.components() {
127 let next_dir = current_dir.join(component);
128 let current_tree = tree_cache.get(¤t_dir).unwrap();
129 if !tree_cache.contains_key(&next_dir) {
130 let tree = current_tree
131 .sub_tree(component)
132 .unwrap_or_else(|| Tree::null(self.store.clone(), next_dir.clone()));
133 let store_tree = tree.data().clone();
134 tree_cache.insert(next_dir.clone(), tree);
135 base_trees.insert(next_dir.clone(), store_tree);
136 }
137 current_dir = next_dir;
138 }
139 };
140 for path in self.overrides.keys() {
141 if let Some(parent) = path.dir() {
142 populate_trees(&parent);
143 }
144 }
145
146 base_trees
147 }
148}