| ... | ... | @@ -663,35 +663,17 @@ fn iterateImpl(self: Dir, first_iter_start_value: bool) Iterator { |
| 663 | 663 | } |
| 664 | 664 | } |
| 665 | 665 | |
| 666 | | pub const Walker = struct { |
| 667 | | stack: std.ArrayListUnmanaged(StackItem), |
| 666 | pub const SelectiveWalker = struct { |
| 667 | stack: std.ArrayListUnmanaged(Walker.StackItem), |
| 668 | 668 | name_buffer: std.ArrayListUnmanaged(u8), |
| 669 | 669 | allocator: Allocator, |
| 670 | 670 | |
| 671 | | pub const Entry = struct { |
| 672 | | /// The containing directory. This can be used to operate directly on `basename` |
| 673 | | /// rather than `path`, avoiding `error.NameTooLong` for deeply nested paths. |
| 674 | | /// The directory remains open until `next` or `deinit` is called. |
| 675 | | dir: Dir, |
| 676 | | basename: [:0]const u8, |
| 677 | | path: [:0]const u8, |
| 678 | | kind: Dir.Entry.Kind, |
| 679 | | }; |
| 680 | | |
| 681 | | const StackItem = struct { |
| 682 | | iter: Dir.Iterator, |
| 683 | | dirname_len: usize, |
| 684 | | }; |
| 685 | | |
| 686 | 671 | /// After each call to this function, and on deinit(), the memory returned |
| 687 | 672 | /// from this function becomes invalid. A copy must be made in order to keep |
| 688 | 673 | /// a reference to the path. |
| 689 | | pub fn next(self: *Walker) !?Walker.Entry { |
| 690 | | const gpa = self.allocator; |
| 691 | | while (self.stack.items.len != 0) { |
| 692 | | // `top` and `containing` become invalid after appending to `self.stack` |
| 693 | | var top = &self.stack.items[self.stack.items.len - 1]; |
| 694 | | var containing = top; |
| 674 | pub fn next(self: *SelectiveWalker) !?Walker.Entry { |
| 675 | while (self.stack.items.len > 0) { |
| 676 | const top = &self.stack.items[self.stack.items.len - 1]; |
| 695 | 677 | var dirname_len = top.dirname_len; |
| 696 | 678 | if (top.iter.next() catch |err| { |
| 697 | 679 | // If we get an error, then we want the user to be able to continue |
| ... | ... | @@ -703,36 +685,22 @@ pub const Walker = struct { |
| 703 | 685 | item.iter.dir.close(); |
| 704 | 686 | } |
| 705 | 687 | return err; |
| 706 | | }) |base| { |
| 688 | }) |entry| { |
| 707 | 689 | self.name_buffer.shrinkRetainingCapacity(dirname_len); |
| 708 | 690 | if (self.name_buffer.items.len != 0) { |
| 709 | | try self.name_buffer.append(gpa, fs.path.sep); |
| 691 | try self.name_buffer.append(self.allocator, fs.path.sep); |
| 710 | 692 | dirname_len += 1; |
| 711 | 693 | } |
| 712 | | try self.name_buffer.ensureUnusedCapacity(gpa, base.name.len + 1); |
| 713 | | self.name_buffer.appendSliceAssumeCapacity(base.name); |
| 694 | try self.name_buffer.ensureUnusedCapacity(self.allocator, entry.name.len + 1); |
| 695 | self.name_buffer.appendSliceAssumeCapacity(entry.name); |
| 714 | 696 | self.name_buffer.appendAssumeCapacity(0); |
| 715 | | if (base.kind == .directory) { |
| 716 | | var new_dir = top.iter.dir.openDir(base.name, .{ .iterate = true }) catch |err| switch (err) { |
| 717 | | error.NameTooLong => unreachable, // no path sep in base.name |
| 718 | | else => |e| return e, |
| 719 | | }; |
| 720 | | { |
| 721 | | errdefer new_dir.close(); |
| 722 | | try self.stack.append(gpa, .{ |
| 723 | | .iter = new_dir.iterateAssumeFirstIteration(), |
| 724 | | .dirname_len = self.name_buffer.items.len - 1, |
| 725 | | }); |
| 726 | | top = &self.stack.items[self.stack.items.len - 1]; |
| 727 | | containing = &self.stack.items[self.stack.items.len - 2]; |
| 728 | | } |
| 729 | | } |
| 730 | | return .{ |
| 731 | | .dir = containing.iter.dir, |
| 697 | const walker_entry: Walker.Entry = .{ |
| 698 | .dir = top.iter.dir, |
| 732 | 699 | .basename = self.name_buffer.items[dirname_len .. self.name_buffer.items.len - 1 :0], |
| 733 | 700 | .path = self.name_buffer.items[0 .. self.name_buffer.items.len - 1 :0], |
| 734 | | .kind = base.kind, |
| 701 | .kind = entry.kind, |
| 735 | 702 | }; |
| 703 | return walker_entry; |
| 736 | 704 | } else { |
| 737 | 705 | var item = self.stack.pop().?; |
| 738 | 706 | if (self.stack.items.len != 0) { |
| ... | ... | @@ -743,20 +711,44 @@ pub const Walker = struct { |
| 743 | 711 | return null; |
| 744 | 712 | } |
| 745 | 713 | |
| 746 | | pub fn deinit(self: *Walker) void { |
| 747 | | const gpa = self.allocator; |
| 748 | | // Close any remaining directories except the initial one (which is always at index 0) |
| 749 | | if (self.stack.items.len > 1) { |
| 750 | | for (self.stack.items[1..]) |*item| { |
| 751 | | item.iter.dir.close(); |
| 714 | /// Traverses into the directory, continuing walking one level down. |
| 715 | pub fn enter(self: *SelectiveWalker, entry: Walker.Entry) !void { |
| 716 | if (entry.kind != .directory) { |
| 717 | @branchHint(.cold); |
| 718 | return; |
| 719 | } |
| 720 | |
| 721 | var new_dir = entry.dir.openDir(entry.basename, .{ .iterate = true }) catch |err| { |
| 722 | switch (err) { |
| 723 | error.NameTooLong => unreachable, |
| 724 | else => |e| return e, |
| 752 | 725 | } |
| 726 | }; |
| 727 | errdefer new_dir.close(); |
| 728 | |
| 729 | try self.stack.append(self.allocator, .{ |
| 730 | .iter = new_dir.iterateAssumeFirstIteration(), |
| 731 | .dirname_len = self.name_buffer.items.len - 1, |
| 732 | }); |
| 733 | } |
| 734 | |
| 735 | pub fn deinit(self: *SelectiveWalker) void { |
| 736 | self.name_buffer.deinit(self.allocator); |
| 737 | self.stack.deinit(self.allocator); |
| 738 | } |
| 739 | |
| 740 | /// Leaves the current directory, continuing walking one level up. |
| 741 | pub fn leave(self: *SelectiveWalker) void { |
| 742 | var item = self.stack.pop().?; |
| 743 | if (self.stack.items.len != 0) { |
| 744 | @branchHint(.likely); |
| 745 | item.iter.dir.close(); |
| 753 | 746 | } |
| 754 | | self.stack.deinit(gpa); |
| 755 | | self.name_buffer.deinit(gpa); |
| 756 | 747 | } |
| 757 | 748 | }; |
| 758 | 749 | |
| 759 | | /// Recursively iterates over a directory. |
| 750 | /// Recursively iterates over a directory, but requires the user to |
| 751 | /// opt-in to recursing into each directory entry. |
| 760 | 752 | /// |
| 761 | 753 | /// `self` must have been opened with `OpenOptions{.iterate = true}`. |
| 762 | 754 | /// |
| ... | ... | @@ -765,7 +757,9 @@ pub const Walker = struct { |
| 765 | 757 | /// The order of returned file system entries is undefined. |
| 766 | 758 | /// |
| 767 | 759 | /// `self` will not be closed after walking it. |
| 768 | | pub fn walk(self: Dir, allocator: Allocator) Allocator.Error!Walker { |
| 760 | /// |
| 761 | /// See also `walk`. |
| 762 | pub fn walkSelectively(self: Dir, allocator: Allocator) !SelectiveWalker { |
| 769 | 763 | var stack: std.ArrayListUnmanaged(Walker.StackItem) = .empty; |
| 770 | 764 | |
| 771 | 765 | try stack.append(allocator, .{ |
| ... | ... | @@ -780,6 +774,64 @@ pub fn walk(self: Dir, allocator: Allocator) Allocator.Error!Walker { |
| 780 | 774 | }; |
| 781 | 775 | } |
| 782 | 776 | |
| 777 | pub const Walker = struct { |
| 778 | inner: SelectiveWalker, |
| 779 | |
| 780 | pub const Entry = struct { |
| 781 | /// The containing directory. This can be used to operate directly on `basename` |
| 782 | /// rather than `path`, avoiding `error.NameTooLong` for deeply nested paths. |
| 783 | /// The directory remains open until `next` or `deinit` is called. |
| 784 | dir: Dir, |
| 785 | basename: [:0]const u8, |
| 786 | path: [:0]const u8, |
| 787 | kind: Dir.Entry.Kind, |
| 788 | }; |
| 789 | |
| 790 | const StackItem = struct { |
| 791 | iter: Dir.Iterator, |
| 792 | dirname_len: usize, |
| 793 | }; |
| 794 | |
| 795 | /// After each call to this function, and on deinit(), the memory returned |
| 796 | /// from this function becomes invalid. A copy must be made in order to keep |
| 797 | /// a reference to the path. |
| 798 | pub fn next(self: *Walker) !?Walker.Entry { |
| 799 | const entry = try self.inner.next(); |
| 800 | if (entry != null and entry.?.kind == .directory) { |
| 801 | try self.inner.enter(entry.?); |
| 802 | } |
| 803 | return entry; |
| 804 | } |
| 805 | |
| 806 | pub fn deinit(self: *Walker) void { |
| 807 | self.inner.deinit(); |
| 808 | } |
| 809 | |
| 810 | /// Leaves the current directory, continuing walking one level up. |
| 811 | /// If the current entry is a directory entry, then the "current directory" |
| 812 | /// is the directory pertaining to the current entry. |
| 813 | pub fn leave(self: *Walker) void { |
| 814 | self.inner.leave(); |
| 815 | } |
| 816 | }; |
| 817 | |
| 818 | /// Recursively iterates over a directory. |
| 819 | /// |
| 820 | /// `self` must have been opened with `OpenOptions{.iterate = true}`. |
| 821 | /// |
| 822 | /// `Walker.deinit` releases allocated memory and directory handles. |
| 823 | /// |
| 824 | /// The order of returned file system entries is undefined. |
| 825 | /// |
| 826 | /// `self` will not be closed after walking it. |
| 827 | /// |
| 828 | /// See also `walkSelectively`. |
| 829 | pub fn walk(self: Dir, allocator: Allocator) Allocator.Error!Walker { |
| 830 | return .{ |
| 831 | .inner = try walkSelectively(self, allocator), |
| 832 | }; |
| 833 | } |
| 834 | |
| 783 | 835 | pub const OpenError = error{ |
| 784 | 836 | FileNotFound, |
| 785 | 837 | NotDir, |