just playing with tangled
at tmp-tutorial 148 lines 4.9 kB view raw
1// Copyright 2020 The Jujutsu Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// https://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15#![allow(missing_docs)] 16 17use std::collections::BTreeMap; 18use std::sync::Arc; 19 20use crate::backend; 21use crate::backend::{TreeId, TreeValue}; 22use crate::repo_path::RepoPath; 23use crate::store::Store; 24use crate::tree::Tree; 25 26#[derive(Debug)] 27enum Override { 28 Tombstone, 29 Replace(TreeValue), 30} 31 32#[derive(Debug)] 33pub struct TreeBuilder { 34 store: Arc<Store>, 35 base_tree_id: TreeId, 36 overrides: BTreeMap<RepoPath, Override>, 37} 38 39impl TreeBuilder { 40 pub fn new(store: Arc<Store>, base_tree_id: TreeId) -> TreeBuilder { 41 let overrides = BTreeMap::new(); 42 TreeBuilder { 43 store, 44 base_tree_id, 45 overrides, 46 } 47 } 48 49 pub fn store(&self) -> &Store { 50 self.store.as_ref() 51 } 52 53 pub fn has_overrides(&self) -> bool { 54 !self.overrides.is_empty() 55 } 56 57 pub fn set(&mut self, path: RepoPath, value: TreeValue) { 58 assert!(!path.is_root()); 59 self.overrides.insert(path, Override::Replace(value)); 60 } 61 62 pub fn remove(&mut self, path: RepoPath) { 63 assert!(!path.is_root()); 64 self.overrides.insert(path, Override::Tombstone); 65 } 66 67 pub fn write_tree(self) -> TreeId { 68 if self.overrides.is_empty() { 69 return self.base_tree_id; 70 } 71 72 let mut trees_to_write = self.get_base_trees(); 73 74 // Update entries in parent trees for file overrides 75 for (path, file_override) in self.overrides { 76 let (dir, basename) = path.split().unwrap(); 77 let tree = trees_to_write.get_mut(&dir).unwrap(); 78 match file_override { 79 Override::Replace(value) => { 80 tree.set(basename.clone(), value); 81 } 82 Override::Tombstone => { 83 tree.remove(basename); 84 } 85 } 86 } 87 88 // Write trees in reverse lexicographical order, starting with trees without 89 // children. 90 let store = &self.store; 91 while let Some((dir, tree)) = trees_to_write.pop_last() { 92 if let Some((parent, basename)) = dir.split() { 93 let parent_tree = trees_to_write.get_mut(&parent).unwrap(); 94 if tree.is_empty() { 95 if let Some(TreeValue::Tree(_)) = parent_tree.value(basename) { 96 parent_tree.remove(basename); 97 } else { 98 // Entry would have been replaced with file (see above) 99 } 100 } else { 101 let tree = store.write_tree(&dir, tree).unwrap(); 102 parent_tree.set(basename.clone(), TreeValue::Tree(tree.id().clone())); 103 } 104 } else { 105 // We're writing the root tree. Write it even if empty. Return its id. 106 assert!(trees_to_write.is_empty()); 107 return store.write_tree(&dir, tree).unwrap().id().clone(); 108 } 109 } 110 111 unreachable!("trees_to_write must contain the root tree"); 112 } 113 114 fn get_base_trees(&self) -> BTreeMap<RepoPath, backend::Tree> { 115 let store = &self.store; 116 let mut tree_cache = { 117 let dir = RepoPath::root(); 118 let tree = store.get_tree(&dir, &self.base_tree_id).unwrap(); 119 BTreeMap::from([(dir, tree)]) 120 }; 121 122 fn populate_trees<'a>( 123 tree_cache: &'a mut BTreeMap<RepoPath, Tree>, 124 store: &Arc<Store>, 125 dir: RepoPath, 126 ) -> &'a Tree { 127 // `if let Some(tree) = ...` doesn't pass lifetime check as of Rust 1.69.0 128 if tree_cache.contains_key(&dir) { 129 return tree_cache.get(&dir).unwrap(); 130 } 131 let (parent, basename) = dir.split().expect("root must be populated"); 132 let tree = populate_trees(tree_cache, store, parent) 133 .sub_tree(basename) 134 .unwrap_or_else(|| Tree::null(store.clone(), dir.clone())); 135 tree_cache.entry(dir).or_insert(tree) 136 } 137 138 for path in self.overrides.keys() { 139 let parent = path.parent().unwrap(); 140 populate_trees(&mut tree_cache, store, parent); 141 } 142 143 tree_cache 144 .into_iter() 145 .map(|(dir, tree)| (dir, tree.data().clone())) 146 .collect() 147 } 148}