| ... | ... | @@ -646,16 +646,17 @@ fn iterateImpl(self: Dir, first_iter_start_value: bool) Iterator { |
| 646 | 646 | } |
| 647 | 647 | |
| 648 | 648 | pub const Walker = struct { |
| 649 | | stack: std.ArrayList(StackItem), |
| 650 | | name_buffer: std.ArrayList(u8), |
| 649 | stack: std.ArrayListUnmanaged(StackItem), |
| 650 | name_buffer: std.ArrayListUnmanaged(u8), |
| 651 | allocator: Allocator, |
| 651 | 652 | |
| 652 | | pub const WalkerEntry = struct { |
| 653 | pub const Entry = struct { |
| 653 | 654 | /// The containing directory. This can be used to operate directly on `basename` |
| 654 | 655 | /// rather than `path`, avoiding `error.NameTooLong` for deeply nested paths. |
| 655 | 656 | /// The directory remains open until `next` or `deinit` is called. |
| 656 | 657 | dir: Dir, |
| 657 | | basename: []const u8, |
| 658 | | path: []const u8, |
| 658 | basename: [:0]const u8, |
| 659 | path: [:0]const u8, |
| 659 | 660 | kind: Dir.Entry.Kind, |
| 660 | 661 | }; |
| 661 | 662 | |
| ... | ... | @@ -667,7 +668,8 @@ pub const Walker = struct { |
| 667 | 668 | /// After each call to this function, and on deinit(), the memory returned |
| 668 | 669 | /// from this function becomes invalid. A copy must be made in order to keep |
| 669 | 670 | /// a reference to the path. |
| 670 | | pub fn next(self: *Walker) !?WalkerEntry { |
| 671 | pub fn next(self: *Walker) !?Walker.Entry { |
| 672 | const gpa = self.allocator; |
| 671 | 673 | while (self.stack.items.len != 0) { |
| 672 | 674 | // `top` and `containing` become invalid after appending to `self.stack` |
| 673 | 675 | var top = &self.stack.items[self.stack.items.len - 1]; |
| ... | ... | @@ -686,10 +688,12 @@ pub const Walker = struct { |
| 686 | 688 | }) |base| { |
| 687 | 689 | self.name_buffer.shrinkRetainingCapacity(dirname_len); |
| 688 | 690 | if (self.name_buffer.items.len != 0) { |
| 689 | | try self.name_buffer.append(fs.path.sep); |
| 691 | try self.name_buffer.append(gpa, fs.path.sep); |
| 690 | 692 | dirname_len += 1; |
| 691 | 693 | } |
| 692 | | try self.name_buffer.appendSlice(base.name); |
| 694 | try self.name_buffer.ensureUnusedCapacity(gpa, base.name.len + 1); |
| 695 | self.name_buffer.appendSliceAssumeCapacity(base.name); |
| 696 | self.name_buffer.appendAssumeCapacity(0); |
| 693 | 697 | if (base.kind == .directory) { |
| 694 | 698 | var new_dir = top.iter.dir.openDir(base.name, .{ .iterate = true }) catch |err| switch (err) { |
| 695 | 699 | error.NameTooLong => unreachable, // no path sep in base.name |
| ... | ... | @@ -697,18 +701,18 @@ pub const Walker = struct { |
| 697 | 701 | }; |
| 698 | 702 | { |
| 699 | 703 | errdefer new_dir.close(); |
| 700 | | try self.stack.append(StackItem{ |
| 704 | try self.stack.append(gpa, .{ |
| 701 | 705 | .iter = new_dir.iterateAssumeFirstIteration(), |
| 702 | | .dirname_len = self.name_buffer.items.len, |
| 706 | .dirname_len = self.name_buffer.items.len - 1, |
| 703 | 707 | }); |
| 704 | 708 | top = &self.stack.items[self.stack.items.len - 1]; |
| 705 | 709 | containing = &self.stack.items[self.stack.items.len - 2]; |
| 706 | 710 | } |
| 707 | 711 | } |
| 708 | | return WalkerEntry{ |
| 712 | return .{ |
| 709 | 713 | .dir = containing.iter.dir, |
| 710 | | .basename = self.name_buffer.items[dirname_len..], |
| 711 | | .path = self.name_buffer.items, |
| 714 | .basename = self.name_buffer.items[dirname_len .. self.name_buffer.items.len - 1 :0], |
| 715 | .path = self.name_buffer.items[0 .. self.name_buffer.items.len - 1 :0], |
| 712 | 716 | .kind = base.kind, |
| 713 | 717 | }; |
| 714 | 718 | } else { |
| ... | ... | @@ -722,37 +726,39 @@ pub const Walker = struct { |
| 722 | 726 | } |
| 723 | 727 | |
| 724 | 728 | pub fn deinit(self: *Walker) void { |
| 729 | const gpa = self.allocator; |
| 725 | 730 | // Close any remaining directories except the initial one (which is always at index 0) |
| 726 | 731 | if (self.stack.items.len > 1) { |
| 727 | 732 | for (self.stack.items[1..]) |*item| { |
| 728 | 733 | item.iter.dir.close(); |
| 729 | 734 | } |
| 730 | 735 | } |
| 731 | | self.stack.deinit(); |
| 732 | | self.name_buffer.deinit(); |
| 736 | self.stack.deinit(gpa); |
| 737 | self.name_buffer.deinit(gpa); |
| 733 | 738 | } |
| 734 | 739 | }; |
| 735 | 740 | |
| 736 | 741 | /// Recursively iterates over a directory. |
| 742 | /// |
| 737 | 743 | /// `self` must have been opened with `OpenDirOptions{.iterate = true}`. |
| 738 | | /// Must call `Walker.deinit` when done. |
| 744 | /// |
| 745 | /// `Walker.deinit` releases allocated memory and directory handles. |
| 746 | /// |
| 739 | 747 | /// The order of returned file system entries is undefined. |
| 748 | /// |
| 740 | 749 | /// `self` will not be closed after walking it. |
| 741 | | pub fn walk(self: Dir, allocator: Allocator) !Walker { |
| 742 | | var name_buffer = std.ArrayList(u8).init(allocator); |
| 743 | | errdefer name_buffer.deinit(); |
| 744 | | |
| 745 | | var stack = std.ArrayList(Walker.StackItem).init(allocator); |
| 746 | | errdefer stack.deinit(); |
| 750 | pub fn walk(self: Dir, allocator: Allocator) Allocator.Error!Walker { |
| 751 | var stack: std.ArrayListUnmanaged(Walker.StackItem) = .{}; |
| 747 | 752 | |
| 748 | | try stack.append(Walker.StackItem{ |
| 753 | try stack.append(allocator, .{ |
| 749 | 754 | .iter = self.iterate(), |
| 750 | 755 | .dirname_len = 0, |
| 751 | 756 | }); |
| 752 | 757 | |
| 753 | | return Walker{ |
| 758 | return .{ |
| 754 | 759 | .stack = stack, |
| 755 | | .name_buffer = name_buffer, |
| 760 | .name_buffer = .{}, |
| 761 | .allocator = allocator, |
| 756 | 762 | }; |
| 757 | 763 | } |
| 758 | 764 | |