##// END OF EJS Templates
rust: clippy pass...
Raphaël Gomès -
r46181:2a169781 default
parent child Browse files
Show More
@@ -1,392 +1,392 b''
1 1 // iter.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::node::{Node, NodeKind};
9 9 use super::tree::Tree;
10 10 use crate::dirstate::dirstate_tree::node::Directory;
11 11 use crate::dirstate::status::Dispatch;
12 12 use crate::utils::hg_path::{hg_path_to_path_buf, HgPath, HgPathBuf};
13 13 use crate::DirstateEntry;
14 14 use std::borrow::Cow;
15 15 use std::collections::VecDeque;
16 16 use std::iter::{FromIterator, FusedIterator};
17 17 use std::path::PathBuf;
18 18
19 19 impl FromIterator<(HgPathBuf, DirstateEntry)> for Tree {
20 20 fn from_iter<T: IntoIterator<Item = (HgPathBuf, DirstateEntry)>>(
21 21 iter: T,
22 22 ) -> Self {
23 23 let mut tree = Self::new();
24 24 for (path, entry) in iter {
25 25 tree.insert(path, entry);
26 26 }
27 27 tree
28 28 }
29 29 }
30 30
31 31 /// Iterator of all entries in the dirstate tree.
32 32 ///
33 33 /// It has no particular ordering.
34 34 pub struct Iter<'a> {
35 35 to_visit: VecDeque<(Cow<'a, [u8]>, &'a Node)>,
36 36 }
37 37
38 38 impl<'a> Iter<'a> {
39 39 pub fn new(node: &'a Node) -> Iter<'a> {
40 40 let mut to_visit = VecDeque::new();
41 41 to_visit.push_back((Cow::Borrowed(&b""[..]), node));
42 42 Self { to_visit }
43 43 }
44 44 }
45 45
46 46 impl<'a> Iterator for Iter<'a> {
47 47 type Item = (HgPathBuf, DirstateEntry);
48 48
49 49 fn next(&mut self) -> Option<Self::Item> {
50 50 while let Some((base_path, node)) = self.to_visit.pop_front() {
51 51 match &node.kind {
52 52 NodeKind::Directory(dir) => {
53 53 add_children_to_visit(
54 54 &mut self.to_visit,
55 55 &base_path,
56 56 &dir,
57 57 );
58 58 if let Some(file) = &dir.was_file {
59 59 return Some((
60 60 HgPathBuf::from_bytes(&base_path),
61 61 file.entry,
62 62 ));
63 63 }
64 64 }
65 65 NodeKind::File(file) => {
66 66 if let Some(dir) = &file.was_directory {
67 67 add_children_to_visit(
68 68 &mut self.to_visit,
69 69 &base_path,
70 70 &dir,
71 71 );
72 72 }
73 73 return Some((
74 74 HgPathBuf::from_bytes(&base_path),
75 75 file.entry,
76 76 ));
77 77 }
78 78 }
79 79 }
80 80 None
81 81 }
82 82 }
83 83
84 84 impl<'a> FusedIterator for Iter<'a> {}
85 85
86 86 /// Iterator of all entries in the dirstate tree, with a special filesystem
87 87 /// handling for the directories containing said entries.
88 88 ///
89 89 /// It checks every directory on-disk to see if it has become a symlink, to
90 90 /// prevent a potential security issue.
91 91 /// Using this information, it may dispatch `status` information early: it
92 92 /// returns canonical paths along with `Shortcut`s, which are either a
93 93 /// `DirstateEntry` or a `Dispatch`, if the fate of said path has already been
94 94 /// determined.
95 95 ///
96 96 /// Like `Iter`, it has no particular ordering.
97 97 pub struct FsIter<'a> {
98 98 root_dir: PathBuf,
99 99 to_visit: VecDeque<(Cow<'a, [u8]>, &'a Node)>,
100 100 shortcuts: VecDeque<(HgPathBuf, StatusShortcut)>,
101 101 }
102 102
103 103 impl<'a> FsIter<'a> {
104 104 pub fn new(node: &'a Node, root_dir: PathBuf) -> FsIter<'a> {
105 105 let mut to_visit = VecDeque::new();
106 106 to_visit.push_back((Cow::Borrowed(&b""[..]), node));
107 107 Self {
108 108 root_dir,
109 109 to_visit,
110 110 shortcuts: Default::default(),
111 111 }
112 112 }
113 113
114 114 /// Mercurial tracks symlinks but *not* what they point to.
115 115 /// If a directory is moved and symlinked:
116 116 ///
117 117 /// ```bash
118 118 /// $ mkdir foo
119 119 /// $ touch foo/a
120 120 /// $ # commit...
121 121 /// $ mv foo bar
122 122 /// $ ln -s bar foo
123 123 /// ```
124 124 /// We need to dispatch the new symlink as `Unknown` and all the
125 125 /// descendents of the directory it replace as `Deleted`.
126 126 fn dispatch_symlinked_directory(
127 127 &mut self,
128 128 path: impl AsRef<HgPath>,
129 129 node: &Node,
130 130 ) {
131 131 let path = path.as_ref();
132 132 self.shortcuts.push_back((
133 133 path.to_owned(),
134 134 StatusShortcut::Dispatch(Dispatch::Unknown),
135 135 ));
136 136 for (file, _) in node.iter() {
137 137 self.shortcuts.push_back((
138 138 path.join(&file),
139 139 StatusShortcut::Dispatch(Dispatch::Deleted),
140 140 ));
141 141 }
142 142 }
143 143
144 144 /// Returns `true` if the canonical `path` of a directory corresponds to a
145 145 /// symlink on disk. It means it was moved and symlinked after the last
146 146 /// dirstate update.
147 147 ///
148 148 /// # Special cases
149 149 ///
150 150 /// Returns `false` for the repository root.
151 151 /// Returns `false` on io error, error handling is outside of the iterator.
152 152 fn directory_became_symlink(&mut self, path: &HgPath) -> bool {
153 153 if path.is_empty() {
154 154 return false;
155 155 }
156 156 let filename_as_path = match hg_path_to_path_buf(&path) {
157 157 Ok(p) => p,
158 158 _ => return false,
159 159 };
160 160 let meta = self.root_dir.join(filename_as_path).symlink_metadata();
161 161 match meta {
162 162 Ok(ref m) if m.file_type().is_symlink() => true,
163 _ => return false,
163 _ => false,
164 164 }
165 165 }
166 166 }
167 167
168 168 /// Returned by `FsIter`, since the `Dispatch` of any given entry may already
169 169 /// be determined during the iteration. This is necessary for performance
170 170 /// reasons, since hierarchical information is needed to `Dispatch` an entire
171 171 /// subtree efficiently.
172 172 #[derive(Debug, Copy, Clone)]
173 173 pub enum StatusShortcut {
174 174 /// A entry in the dirstate for further inspection
175 175 Entry(DirstateEntry),
176 176 /// The result of the status of the corresponding file
177 177 Dispatch(Dispatch),
178 178 }
179 179
180 180 impl<'a> Iterator for FsIter<'a> {
181 181 type Item = (HgPathBuf, StatusShortcut);
182 182
183 183 fn next(&mut self) -> Option<Self::Item> {
184 184 // If any paths have already been `Dispatch`-ed, return them
185 while let Some(res) = self.shortcuts.pop_front() {
185 if let Some(res) = self.shortcuts.pop_front() {
186 186 return Some(res);
187 187 }
188 188
189 189 while let Some((base_path, node)) = self.to_visit.pop_front() {
190 190 match &node.kind {
191 191 NodeKind::Directory(dir) => {
192 192 let canonical_path = HgPath::new(&base_path);
193 193 if self.directory_became_symlink(canonical_path) {
194 194 // Potential security issue, don't do a normal
195 195 // traversal, force the results.
196 196 self.dispatch_symlinked_directory(
197 197 canonical_path,
198 198 &node,
199 199 );
200 200 continue;
201 201 }
202 202 add_children_to_visit(
203 203 &mut self.to_visit,
204 204 &base_path,
205 205 &dir,
206 206 );
207 207 if let Some(file) = &dir.was_file {
208 208 return Some((
209 209 HgPathBuf::from_bytes(&base_path),
210 210 StatusShortcut::Entry(file.entry),
211 211 ));
212 212 }
213 213 }
214 214 NodeKind::File(file) => {
215 215 if let Some(dir) = &file.was_directory {
216 216 add_children_to_visit(
217 217 &mut self.to_visit,
218 218 &base_path,
219 219 &dir,
220 220 );
221 221 }
222 222 return Some((
223 223 HgPathBuf::from_bytes(&base_path),
224 224 StatusShortcut::Entry(file.entry),
225 225 ));
226 226 }
227 227 }
228 228 }
229 229
230 230 None
231 231 }
232 232 }
233 233
234 234 impl<'a> FusedIterator for FsIter<'a> {}
235 235
236 236 fn join_path<'a, 'b>(path: &'a [u8], other: &'b [u8]) -> Cow<'b, [u8]> {
237 237 if path.is_empty() {
238 238 other.into()
239 239 } else {
240 240 [path, &b"/"[..], other].concat().into()
241 241 }
242 242 }
243 243
244 244 /// Adds all children of a given directory `dir` to the visit queue `to_visit`
245 245 /// prefixed by a `base_path`.
246 246 fn add_children_to_visit<'a>(
247 247 to_visit: &mut VecDeque<(Cow<'a, [u8]>, &'a Node)>,
248 248 base_path: &[u8],
249 249 dir: &'a Directory,
250 250 ) {
251 251 to_visit.extend(dir.children.iter().map(|(path, child)| {
252 252 let full_path = join_path(&base_path, &path);
253 (Cow::from(full_path), child)
253 (full_path, child)
254 254 }));
255 255 }
256 256
257 257 #[cfg(test)]
258 258 mod tests {
259 259 use super::*;
260 260 use crate::utils::hg_path::HgPath;
261 261 use crate::{EntryState, FastHashMap};
262 262 use std::collections::HashSet;
263 263
264 264 #[test]
265 265 fn test_iteration() {
266 266 let mut tree = Tree::new();
267 267
268 268 assert_eq!(
269 269 tree.insert(
270 270 HgPathBuf::from_bytes(b"foo/bar"),
271 271 DirstateEntry {
272 272 state: EntryState::Merged,
273 273 mode: 41,
274 274 mtime: 42,
275 275 size: 43,
276 276 }
277 277 ),
278 278 None
279 279 );
280 280
281 281 assert_eq!(
282 282 tree.insert(
283 283 HgPathBuf::from_bytes(b"foo2"),
284 284 DirstateEntry {
285 285 state: EntryState::Merged,
286 286 mode: 40,
287 287 mtime: 41,
288 288 size: 42,
289 289 }
290 290 ),
291 291 None
292 292 );
293 293
294 294 assert_eq!(
295 295 tree.insert(
296 296 HgPathBuf::from_bytes(b"foo/baz"),
297 297 DirstateEntry {
298 298 state: EntryState::Normal,
299 299 mode: 0,
300 300 mtime: 0,
301 301 size: 0,
302 302 }
303 303 ),
304 304 None
305 305 );
306 306
307 307 assert_eq!(
308 308 tree.insert(
309 309 HgPathBuf::from_bytes(b"foo/bap/nested"),
310 310 DirstateEntry {
311 311 state: EntryState::Normal,
312 312 mode: 0,
313 313 mtime: 0,
314 314 size: 0,
315 315 }
316 316 ),
317 317 None
318 318 );
319 319
320 320 assert_eq!(tree.len(), 4);
321 321
322 322 let results: HashSet<_> =
323 323 tree.iter().map(|(c, _)| c.to_owned()).collect();
324 324 dbg!(&results);
325 325 assert!(results.contains(HgPath::new(b"foo2")));
326 326 assert!(results.contains(HgPath::new(b"foo/bar")));
327 327 assert!(results.contains(HgPath::new(b"foo/baz")));
328 328 assert!(results.contains(HgPath::new(b"foo/bap/nested")));
329 329
330 330 let mut iter = tree.iter();
331 331 assert!(iter.next().is_some());
332 332 assert!(iter.next().is_some());
333 333 assert!(iter.next().is_some());
334 334 assert!(iter.next().is_some());
335 335 assert_eq!(None, iter.next());
336 336 assert_eq!(None, iter.next());
337 337 drop(iter);
338 338
339 339 assert_eq!(
340 340 tree.insert(
341 341 HgPathBuf::from_bytes(b"foo/bap/nested/a"),
342 342 DirstateEntry {
343 343 state: EntryState::Normal,
344 344 mode: 0,
345 345 mtime: 0,
346 346 size: 0,
347 347 }
348 348 ),
349 349 None
350 350 );
351 351
352 352 let results: FastHashMap<_, _> = tree.iter().collect();
353 353 assert!(results.contains_key(HgPath::new(b"foo2")));
354 354 assert!(results.contains_key(HgPath::new(b"foo/bar")));
355 355 assert!(results.contains_key(HgPath::new(b"foo/baz")));
356 356 // Is a dir but `was_file`, so it's listed as a removed file
357 357 assert!(results.contains_key(HgPath::new(b"foo/bap/nested")));
358 358 assert!(results.contains_key(HgPath::new(b"foo/bap/nested/a")));
359 359
360 360 // insert removed file (now directory) after nested file
361 361 assert_eq!(
362 362 tree.insert(
363 363 HgPathBuf::from_bytes(b"a/a"),
364 364 DirstateEntry {
365 365 state: EntryState::Normal,
366 366 mode: 0,
367 367 mtime: 0,
368 368 size: 0,
369 369 }
370 370 ),
371 371 None
372 372 );
373 373
374 374 // `insert` returns `None` for a directory
375 375 assert_eq!(
376 376 tree.insert(
377 377 HgPathBuf::from_bytes(b"a"),
378 378 DirstateEntry {
379 379 state: EntryState::Removed,
380 380 mode: 0,
381 381 mtime: 0,
382 382 size: 0,
383 383 }
384 384 ),
385 385 None
386 386 );
387 387
388 388 let results: FastHashMap<_, _> = tree.iter().collect();
389 389 assert!(results.contains_key(HgPath::new(b"a")));
390 390 assert!(results.contains_key(HgPath::new(b"a/a")));
391 391 }
392 392 }
@@ -1,395 +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 60 if let NodeKind::File(file) = &mut self.kind {
61 61 if tail.is_empty() && head.is_empty() {
62 62 // We're modifying the current file
63 63 let new = Self {
64 64 kind: NodeKind::File(File {
65 65 entry: new_entry,
66 66 ..file.clone()
67 67 }),
68 68 };
69 69 return InsertResult {
70 70 did_insert: false,
71 71 old_entry: Some(std::mem::replace(self, new)),
72 72 };
73 73 } else {
74 74 match file.entry.state {
75 75 // Only replace the current file with a directory if it's
76 76 // marked as `Removed`
77 77 EntryState::Removed => {
78 78 self.kind = NodeKind::Directory(Directory {
79 79 was_file: Some(Box::from(file.clone())),
80 80 children: Default::default(),
81 81 })
82 82 }
83 83 _ => {
84 84 return Node::insert_in_file(
85 85 file, new_entry, head, tail,
86 86 )
87 87 }
88 88 }
89 89 }
90 90 }
91 91
92 92 match &mut self.kind {
93 93 NodeKind::Directory(directory) => {
94 return Node::insert_in_directory(
94 Node::insert_in_directory(
95 95 directory, new_entry, head, tail,
96 );
96 )
97 97 }
98 98 NodeKind::File(_) => {
99 99 unreachable!("The file case has already been handled")
100 100 }
101 101 }
102 102 }
103 103
104 104 /// The current file still exists and is not marked as `Removed`.
105 105 /// Insert the entry in its `was_directory`.
106 106 fn insert_in_file(
107 107 file: &mut File,
108 108 new_entry: DirstateEntry,
109 109 head: &[u8],
110 110 tail: &[u8],
111 111 ) -> InsertResult {
112 112 if let Some(d) = &mut file.was_directory {
113 113 Node::insert_in_directory(d, new_entry, head, tail)
114 114 } else {
115 115 let mut dir = Directory {
116 116 was_file: None,
117 117 children: FastHashMap::default(),
118 118 };
119 119 let res =
120 120 Node::insert_in_directory(&mut dir, new_entry, head, tail);
121 121 file.was_directory = Some(Box::new(dir));
122 122 res
123 123 }
124 124 }
125 125
126 126 /// Insert an entry in the subtree of `directory`
127 127 fn insert_in_directory(
128 128 directory: &mut Directory,
129 129 new_entry: DirstateEntry,
130 130 head: &[u8],
131 131 tail: &[u8],
132 132 ) -> InsertResult {
133 133 let mut res = InsertResult::default();
134 134
135 135 if let Some(node) = directory.children.get_mut(head) {
136 136 // Node exists
137 137 match &mut node.kind {
138 138 NodeKind::Directory(subdir) => {
139 139 if tail.is_empty() {
140 140 let becomes_file = Self {
141 141 kind: NodeKind::File(File {
142 142 was_directory: Some(Box::from(subdir.clone())),
143 143 entry: new_entry,
144 144 }),
145 145 };
146 146 let old_entry = directory
147 147 .children
148 148 .insert(head.to_owned(), becomes_file);
149 149 return InsertResult {
150 150 did_insert: true,
151 151 old_entry,
152 152 };
153 153 } else {
154 154 res = node.insert(tail, new_entry);
155 155 }
156 156 }
157 157 NodeKind::File(_) => {
158 158 res = node.insert(tail, new_entry);
159 159 }
160 160 }
161 161 } else if tail.is_empty() {
162 162 // File does not already exist
163 163 directory.children.insert(
164 164 head.to_owned(),
165 165 Self {
166 166 kind: NodeKind::File(File {
167 167 was_directory: None,
168 168 entry: new_entry,
169 169 }),
170 170 },
171 171 );
172 172 res.did_insert = true;
173 173 } else {
174 174 // Directory does not already exist
175 175 let mut nested = Self {
176 176 kind: NodeKind::Directory(Directory {
177 177 was_file: None,
178 178 children: Default::default(),
179 179 }),
180 180 };
181 181 res = nested.insert(tail, new_entry);
182 182 directory.children.insert(head.to_owned(), nested);
183 183 }
184 184 res
185 185 }
186 186
187 187 /// Removes an entry from the tree, returns a `RemoveResult`.
188 188 pub fn remove(&mut self, path: &[u8]) -> RemoveResult {
189 189 let empty_result = RemoveResult::default();
190 190 if path.is_empty() {
191 191 return empty_result;
192 192 }
193 193 let mut split = path.splitn(2, |&c| c == b'/');
194 194 let head = split.next();
195 195 let tail = split.next().unwrap_or(b"");
196 196
197 197 let head = match head {
198 198 None => {
199 199 return empty_result;
200 200 }
201 201 Some(h) => h,
202 202 };
203 203 if head == path {
204 204 match &mut self.kind {
205 205 NodeKind::Directory(d) => {
206 206 return Node::remove_from_directory(head, d);
207 207 }
208 208 NodeKind::File(f) => {
209 209 if let Some(d) = &mut f.was_directory {
210 210 let RemoveResult { old_entry, .. } =
211 211 Node::remove_from_directory(head, d);
212 212 return RemoveResult {
213 213 cleanup: false,
214 214 old_entry,
215 215 };
216 216 }
217 217 }
218 218 }
219 219 empty_result
220 220 } else {
221 221 // Look into the dirs
222 222 match &mut self.kind {
223 223 NodeKind::Directory(d) => {
224 224 if let Some(child) = d.children.get_mut(head) {
225 225 let mut res = child.remove(tail);
226 226 if res.cleanup {
227 227 d.children.remove(head);
228 228 }
229 229 res.cleanup =
230 d.children.len() == 0 && d.was_file.is_none();
230 d.children.is_empty() && d.was_file.is_none();
231 231 res
232 232 } else {
233 233 empty_result
234 234 }
235 235 }
236 236 NodeKind::File(f) => {
237 237 if let Some(d) = &mut f.was_directory {
238 238 if let Some(child) = d.children.get_mut(head) {
239 239 let RemoveResult { cleanup, old_entry } =
240 240 child.remove(tail);
241 241 if cleanup {
242 242 d.children.remove(head);
243 243 }
244 if d.children.len() == 0 && d.was_file.is_none() {
244 if d.children.is_empty() && d.was_file.is_none() {
245 245 f.was_directory = None;
246 246 }
247 247
248 248 return RemoveResult {
249 249 cleanup: false,
250 250 old_entry,
251 251 };
252 252 }
253 253 }
254 254 empty_result
255 255 }
256 256 }
257 257 }
258 258 }
259 259
260 260 fn remove_from_directory(head: &[u8], d: &mut Directory) -> RemoveResult {
261 261 if let Some(node) = d.children.get_mut(head) {
262 262 return match &mut node.kind {
263 263 NodeKind::Directory(d) => {
264 264 if let Some(f) = &mut d.was_file {
265 265 let entry = f.entry;
266 266 d.was_file = None;
267 267 RemoveResult {
268 268 cleanup: false,
269 269 old_entry: Some(entry),
270 270 }
271 271 } else {
272 272 RemoveResult::default()
273 273 }
274 274 }
275 275 NodeKind::File(f) => {
276 276 let entry = f.entry;
277 277 let mut cleanup = false;
278 278 match &f.was_directory {
279 279 None => {
280 280 if d.children.len() == 1 {
281 281 cleanup = true;
282 282 }
283 283 d.children.remove(head);
284 284 }
285 285 Some(dir) => {
286 286 node.kind = NodeKind::Directory(*dir.clone());
287 287 }
288 288 }
289 289
290 290 RemoveResult {
291 cleanup: cleanup,
291 cleanup,
292 292 old_entry: Some(entry),
293 293 }
294 294 }
295 295 };
296 296 }
297 297 RemoveResult::default()
298 298 }
299 299
300 300 pub fn get(&self, path: &[u8]) -> Option<&Node> {
301 301 if path.is_empty() {
302 302 return Some(&self);
303 303 }
304 304 let mut split = path.splitn(2, |&c| c == b'/');
305 305 let head = split.next();
306 306 let tail = split.next().unwrap_or(b"");
307 307
308 308 let head = match head {
309 309 None => {
310 310 return Some(&self);
311 311 }
312 312 Some(h) => h,
313 313 };
314 314 match &self.kind {
315 315 NodeKind::Directory(d) => {
316 316 if let Some(child) = d.children.get(head) {
317 317 return child.get(tail);
318 318 }
319 319 }
320 320 NodeKind::File(f) => {
321 321 if let Some(d) = &f.was_directory {
322 322 if let Some(child) = d.children.get(head) {
323 323 return child.get(tail);
324 324 }
325 325 }
326 326 }
327 327 }
328 328
329 329 None
330 330 }
331 331
332 332 pub fn get_mut(&mut self, path: &[u8]) -> Option<&mut NodeKind> {
333 333 if path.is_empty() {
334 334 return Some(&mut self.kind);
335 335 }
336 336 let mut split = path.splitn(2, |&c| c == b'/');
337 337 let head = split.next();
338 338 let tail = split.next().unwrap_or(b"");
339 339
340 340 let head = match head {
341 341 None => {
342 342 return Some(&mut self.kind);
343 343 }
344 344 Some(h) => h,
345 345 };
346 346 match &mut self.kind {
347 347 NodeKind::Directory(d) => {
348 348 if let Some(child) = d.children.get_mut(head) {
349 349 return child.get_mut(tail);
350 350 }
351 351 }
352 352 NodeKind::File(f) => {
353 353 if let Some(d) = &mut f.was_directory {
354 354 if let Some(child) = d.children.get_mut(head) {
355 355 return child.get_mut(tail);
356 356 }
357 357 }
358 358 }
359 359 }
360 360
361 361 None
362 362 }
363 363
364 364 pub fn iter(&self) -> Iter {
365 365 Iter::new(self)
366 366 }
367 367 }
368 368
369 369 /// Information returned to the caller of an `insert` operation for integrity.
370 370 #[derive(Debug, Default)]
371 371 pub struct InsertResult {
372 372 /// Whether the insertion resulted in an actual insertion and not an
373 373 /// update
374 374 pub(super) did_insert: bool,
375 375 /// The entry that was replaced, if it exists
376 376 pub(super) old_entry: Option<Node>,
377 377 }
378 378
379 379 /// Information returned to the caller of a `remove` operation integrity.
380 380 #[derive(Debug, Default)]
381 381 pub struct RemoveResult {
382 382 /// If the caller needs to remove the current node
383 383 pub(super) cleanup: bool,
384 384 /// The entry that was replaced, if it exists
385 385 pub(super) old_entry: Option<DirstateEntry>,
386 386 }
387 387
388 388 impl<'a> IntoIterator for &'a Node {
389 389 type Item = (HgPathBuf, DirstateEntry);
390 390 type IntoIter = Iter<'a>;
391 391
392 392 fn into_iter(self) -> Self::IntoIter {
393 393 self.iter()
394 394 }
395 395 }
General Comments 0
You need to be logged in to leave comments. Login now