##// END OF EJS Templates
dirstate-tree: move a conditional in an explicit boolean...
marmoute -
r46302:510995e2 default
parent child Browse files
Show More
@@ -1,393 +1,395 b''
1 1 // node.rs
2 2 //
3 3 // Copyright 2020, Raphaël Gomès <rgomes@octobus.net>
4 4 //
5 5 // This software may be used and distributed according to the terms of the
6 6 // GNU General Public License version 2 or any later version.
7 7
8 8 use super::iter::Iter;
9 9 use crate::utils::hg_path::HgPathBuf;
10 10 use crate::{DirstateEntry, EntryState, FastHashMap};
11 11
12 12 /// Represents a filesystem directory in the dirstate tree
13 13 #[derive(Debug, Default, Clone, PartialEq)]
14 14 pub struct Directory {
15 15 /// Contains the old file information if it existed between changesets.
16 16 /// Happens if a file `foo` is marked as removed, removed from the
17 17 /// filesystem then a directory `foo` is created and at least one of its
18 18 /// descendents is added to Mercurial.
19 19 pub(super) was_file: Option<Box<File>>,
20 20 pub(super) children: FastHashMap<Vec<u8>, Node>,
21 21 }
22 22
23 23 /// Represents a filesystem file (or symlink) in the dirstate tree
24 24 #[derive(Debug, Clone, PartialEq)]
25 25 pub struct File {
26 26 /// Contains the old structure if it existed between changesets.
27 27 /// Happens all descendents of `foo` marked as removed and removed from
28 28 /// the filesystem, then a file `foo` is created and added to Mercurial.
29 29 pub(super) was_directory: Option<Box<Directory>>,
30 30 pub(super) entry: DirstateEntry,
31 31 }
32 32
33 33 #[derive(Debug, Clone, PartialEq)]
34 34 pub enum NodeKind {
35 35 Directory(Directory),
36 36 File(File),
37 37 }
38 38
39 39 #[derive(Debug, Default, Clone, PartialEq)]
40 40 pub struct Node {
41 41 pub kind: NodeKind,
42 42 }
43 43
44 44 impl Default for NodeKind {
45 45 fn default() -> Self {
46 46 NodeKind::Directory(Default::default())
47 47 }
48 48 }
49 49
50 50 impl Node {
51 51 pub fn insert(
52 52 &mut self,
53 53 path: &[u8],
54 54 new_entry: DirstateEntry,
55 55 ) -> InsertResult {
56 56 let mut split = path.splitn(2, |&c| c == b'/');
57 57 let head = split.next().unwrap_or(b"");
58 58 let tail = split.next().unwrap_or(b"");
59 59
60 // Are we're modifying the current file ? Is the the end of the path ?
61 let is_current_file = tail.is_empty() && head.is_empty();
62
60 63 if let NodeKind::File(file) = &mut self.kind {
61 if tail.is_empty() && head.is_empty() {
62 // We're modifying the current file
64 if is_current_file {
63 65 let new = Self {
64 66 kind: NodeKind::File(File {
65 67 entry: new_entry,
66 68 ..file.clone()
67 69 }),
68 70 };
69 71 return InsertResult {
70 72 did_insert: false,
71 73 old_entry: Some(std::mem::replace(self, new)),
72 74 };
73 75 } else {
74 76 match file.entry.state {
75 77 // Only replace the current file with a directory if it's
76 78 // marked as `Removed`
77 79 EntryState::Removed => {
78 80 self.kind = NodeKind::Directory(Directory {
79 81 was_file: Some(Box::from(file.clone())),
80 82 children: Default::default(),
81 83 })
82 84 }
83 85 _ => {
84 86 return Node::insert_in_file(
85 87 file, new_entry, head, tail,
86 88 )
87 89 }
88 90 }
89 91 }
90 92 }
91 93
92 94 match &mut self.kind {
93 95 NodeKind::Directory(directory) => {
94 96 Node::insert_in_directory(directory, new_entry, head, tail)
95 97 }
96 98 NodeKind::File(_) => {
97 99 unreachable!("The file case has already been handled")
98 100 }
99 101 }
100 102 }
101 103
102 104 /// The current file still exists and is not marked as `Removed`.
103 105 /// Insert the entry in its `was_directory`.
104 106 fn insert_in_file(
105 107 file: &mut File,
106 108 new_entry: DirstateEntry,
107 109 head: &[u8],
108 110 tail: &[u8],
109 111 ) -> InsertResult {
110 112 if let Some(d) = &mut file.was_directory {
111 113 Node::insert_in_directory(d, new_entry, head, tail)
112 114 } else {
113 115 let mut dir = Directory {
114 116 was_file: None,
115 117 children: FastHashMap::default(),
116 118 };
117 119 let res =
118 120 Node::insert_in_directory(&mut dir, new_entry, head, tail);
119 121 file.was_directory = Some(Box::new(dir));
120 122 res
121 123 }
122 124 }
123 125
124 126 /// Insert an entry in the subtree of `directory`
125 127 fn insert_in_directory(
126 128 directory: &mut Directory,
127 129 new_entry: DirstateEntry,
128 130 head: &[u8],
129 131 tail: &[u8],
130 132 ) -> InsertResult {
131 133 let mut res = InsertResult::default();
132 134
133 135 if let Some(node) = directory.children.get_mut(head) {
134 136 // Node exists
135 137 match &mut node.kind {
136 138 NodeKind::Directory(subdir) => {
137 139 if tail.is_empty() {
138 140 let becomes_file = Self {
139 141 kind: NodeKind::File(File {
140 142 was_directory: Some(Box::from(subdir.clone())),
141 143 entry: new_entry,
142 144 }),
143 145 };
144 146 let old_entry = directory
145 147 .children
146 148 .insert(head.to_owned(), becomes_file);
147 149 return InsertResult {
148 150 did_insert: true,
149 151 old_entry,
150 152 };
151 153 } else {
152 154 res = node.insert(tail, new_entry);
153 155 }
154 156 }
155 157 NodeKind::File(_) => {
156 158 res = node.insert(tail, new_entry);
157 159 }
158 160 }
159 161 } else if tail.is_empty() {
160 162 // File does not already exist
161 163 directory.children.insert(
162 164 head.to_owned(),
163 165 Self {
164 166 kind: NodeKind::File(File {
165 167 was_directory: None,
166 168 entry: new_entry,
167 169 }),
168 170 },
169 171 );
170 172 res.did_insert = true;
171 173 } else {
172 174 // Directory does not already exist
173 175 let mut nested = Self {
174 176 kind: NodeKind::Directory(Directory {
175 177 was_file: None,
176 178 children: Default::default(),
177 179 }),
178 180 };
179 181 res = nested.insert(tail, new_entry);
180 182 directory.children.insert(head.to_owned(), nested);
181 183 }
182 184 res
183 185 }
184 186
185 187 /// Removes an entry from the tree, returns a `RemoveResult`.
186 188 pub fn remove(&mut self, path: &[u8]) -> RemoveResult {
187 189 let empty_result = RemoveResult::default();
188 190 if path.is_empty() {
189 191 return empty_result;
190 192 }
191 193 let mut split = path.splitn(2, |&c| c == b'/');
192 194 let head = split.next();
193 195 let tail = split.next().unwrap_or(b"");
194 196
195 197 let head = match head {
196 198 None => {
197 199 return empty_result;
198 200 }
199 201 Some(h) => h,
200 202 };
201 203 if head == path {
202 204 match &mut self.kind {
203 205 NodeKind::Directory(d) => {
204 206 return Node::remove_from_directory(head, d);
205 207 }
206 208 NodeKind::File(f) => {
207 209 if let Some(d) = &mut f.was_directory {
208 210 let RemoveResult { old_entry, .. } =
209 211 Node::remove_from_directory(head, d);
210 212 return RemoveResult {
211 213 cleanup: false,
212 214 old_entry,
213 215 };
214 216 }
215 217 }
216 218 }
217 219 empty_result
218 220 } else {
219 221 // Look into the dirs
220 222 match &mut self.kind {
221 223 NodeKind::Directory(d) => {
222 224 if let Some(child) = d.children.get_mut(head) {
223 225 let mut res = child.remove(tail);
224 226 if res.cleanup {
225 227 d.children.remove(head);
226 228 }
227 229 res.cleanup =
228 230 d.children.is_empty() && d.was_file.is_none();
229 231 res
230 232 } else {
231 233 empty_result
232 234 }
233 235 }
234 236 NodeKind::File(f) => {
235 237 if let Some(d) = &mut f.was_directory {
236 238 if let Some(child) = d.children.get_mut(head) {
237 239 let RemoveResult { cleanup, old_entry } =
238 240 child.remove(tail);
239 241 if cleanup {
240 242 d.children.remove(head);
241 243 }
242 244 if d.children.is_empty() && d.was_file.is_none() {
243 245 f.was_directory = None;
244 246 }
245 247
246 248 return RemoveResult {
247 249 cleanup: false,
248 250 old_entry,
249 251 };
250 252 }
251 253 }
252 254 empty_result
253 255 }
254 256 }
255 257 }
256 258 }
257 259
258 260 fn remove_from_directory(head: &[u8], d: &mut Directory) -> RemoveResult {
259 261 if let Some(node) = d.children.get_mut(head) {
260 262 return match &mut node.kind {
261 263 NodeKind::Directory(d) => {
262 264 if let Some(f) = &mut d.was_file {
263 265 let entry = f.entry;
264 266 d.was_file = None;
265 267 RemoveResult {
266 268 cleanup: false,
267 269 old_entry: Some(entry),
268 270 }
269 271 } else {
270 272 RemoveResult::default()
271 273 }
272 274 }
273 275 NodeKind::File(f) => {
274 276 let entry = f.entry;
275 277 let mut cleanup = false;
276 278 match &f.was_directory {
277 279 None => {
278 280 if d.children.len() == 1 {
279 281 cleanup = true;
280 282 }
281 283 d.children.remove(head);
282 284 }
283 285 Some(dir) => {
284 286 node.kind = NodeKind::Directory(*dir.clone());
285 287 }
286 288 }
287 289
288 290 RemoveResult {
289 291 cleanup,
290 292 old_entry: Some(entry),
291 293 }
292 294 }
293 295 };
294 296 }
295 297 RemoveResult::default()
296 298 }
297 299
298 300 pub fn get(&self, path: &[u8]) -> Option<&Node> {
299 301 if path.is_empty() {
300 302 return Some(&self);
301 303 }
302 304 let mut split = path.splitn(2, |&c| c == b'/');
303 305 let head = split.next();
304 306 let tail = split.next().unwrap_or(b"");
305 307
306 308 let head = match head {
307 309 None => {
308 310 return Some(&self);
309 311 }
310 312 Some(h) => h,
311 313 };
312 314 match &self.kind {
313 315 NodeKind::Directory(d) => {
314 316 if let Some(child) = d.children.get(head) {
315 317 return child.get(tail);
316 318 }
317 319 }
318 320 NodeKind::File(f) => {
319 321 if let Some(d) = &f.was_directory {
320 322 if let Some(child) = d.children.get(head) {
321 323 return child.get(tail);
322 324 }
323 325 }
324 326 }
325 327 }
326 328
327 329 None
328 330 }
329 331
330 332 pub fn get_mut(&mut self, path: &[u8]) -> Option<&mut NodeKind> {
331 333 if path.is_empty() {
332 334 return Some(&mut self.kind);
333 335 }
334 336 let mut split = path.splitn(2, |&c| c == b'/');
335 337 let head = split.next();
336 338 let tail = split.next().unwrap_or(b"");
337 339
338 340 let head = match head {
339 341 None => {
340 342 return Some(&mut self.kind);
341 343 }
342 344 Some(h) => h,
343 345 };
344 346 match &mut self.kind {
345 347 NodeKind::Directory(d) => {
346 348 if let Some(child) = d.children.get_mut(head) {
347 349 return child.get_mut(tail);
348 350 }
349 351 }
350 352 NodeKind::File(f) => {
351 353 if let Some(d) = &mut f.was_directory {
352 354 if let Some(child) = d.children.get_mut(head) {
353 355 return child.get_mut(tail);
354 356 }
355 357 }
356 358 }
357 359 }
358 360
359 361 None
360 362 }
361 363
362 364 pub fn iter(&self) -> Iter {
363 365 Iter::new(self)
364 366 }
365 367 }
366 368
367 369 /// Information returned to the caller of an `insert` operation for integrity.
368 370 #[derive(Debug, Default)]
369 371 pub struct InsertResult {
370 372 /// Whether the insertion resulted in an actual insertion and not an
371 373 /// update
372 374 pub(super) did_insert: bool,
373 375 /// The entry that was replaced, if it exists
374 376 pub(super) old_entry: Option<Node>,
375 377 }
376 378
377 379 /// Information returned to the caller of a `remove` operation integrity.
378 380 #[derive(Debug, Default)]
379 381 pub struct RemoveResult {
380 382 /// If the caller needs to remove the current node
381 383 pub(super) cleanup: bool,
382 384 /// The entry that was replaced, if it exists
383 385 pub(super) old_entry: Option<DirstateEntry>,
384 386 }
385 387
386 388 impl<'a> IntoIterator for &'a Node {
387 389 type Item = (HgPathBuf, DirstateEntry);
388 390 type IntoIter = Iter<'a>;
389 391
390 392 fn into_iter(self) -> Self::IntoIter {
391 393 self.iter()
392 394 }
393 395 }
General Comments 0
You need to be logged in to leave comments. Login now