| ... | @@ -151,6 +151,99 @@ pub const Iterator = struct { | ... | @@ -151,6 +151,99 @@ pub const Iterator = struct { |
| 151 | }; | 151 | }; |
| 152 | }; | 152 | }; |
| 153 | | 153 | |
| | 154 | pub fn walk(self: Dir, allocator: std.mem.Allocator) !Walker { |
| | 155 | var stack: std.ArrayListUnmanaged(Walker.StackItem) = .empty; |
| | 156 | |
| | 157 | try stack.append(allocator, .{ |
| | 158 | .iter = self.iterate(), |
| | 159 | .dirname_len = 0, |
| | 160 | }); |
| | 161 | |
| | 162 | return .{ |
| | 163 | .allocator = allocator, |
| | 164 | .stack = stack, |
| | 165 | .name_buffer = .empty, |
| | 166 | }; |
| | 167 | } |
| | 168 | |
| | 169 | pub const Walker = struct { |
| | 170 | allocator: std.mem.Allocator, |
| | 171 | stack: std.ArrayListUnmanaged(StackItem), |
| | 172 | name_buffer: std.ArrayListUnmanaged(u8), |
| | 173 | |
| | 174 | pub const Entry = struct { |
| | 175 | dir: Dir, |
| | 176 | basename: [:0]const u8, |
| | 177 | path: [:0]const u8, |
| | 178 | type: sys.DT, |
| | 179 | }; |
| | 180 | |
| | 181 | const StackItem = struct { |
| | 182 | iter: Dir.Iterator, |
| | 183 | dirname_len: usize, |
| | 184 | }; |
| | 185 | |
| | 186 | pub fn next(self: *Walker) !?Walker.Entry { |
| | 187 | const gpa = self.allocator; |
| | 188 | while (self.stack.items.len != 0) { |
| | 189 | var top = &self.stack.items[self.stack.items.len - 1]; |
| | 190 | var containing = top; |
| | 191 | var dirname_len = top.dirname_len; |
| | 192 | if (top.iter.next() catch |err| { |
| | 193 | var item = self.stack.pop().?; |
| | 194 | if (self.stack.items.len != 0) item.iter.dir.close(); |
| | 195 | return err; |
| | 196 | }) |base| { |
| | 197 | self.name_buffer.shrinkRetainingCapacity(dirname_len); |
| | 198 | if (self.name_buffer.items.len != 0) { |
| | 199 | try self.name_buffer.append(gpa, std.fs.path.sep); |
| | 200 | dirname_len += 1; |
| | 201 | } |
| | 202 | try self.name_buffer.ensureUnusedCapacity(gpa, base.name.len + 1); |
| | 203 | self.name_buffer.appendSliceAssumeCapacity(base.name); |
| | 204 | self.name_buffer.appendAssumeCapacity(0); |
| | 205 | if (base.type == .DIR) { |
| | 206 | var new_dir = top.iter.dir.openDir(base.name, .{}) catch |err| switch (err) { |
| | 207 | error.ENAMETOOLONG => unreachable, // no path sep in base.name |
| | 208 | else => |e| return e, |
| | 209 | }; |
| | 210 | { |
| | 211 | errdefer new_dir.close(); |
| | 212 | try self.stack.append(gpa, .{ |
| | 213 | .iter = new_dir.iterate(), // was iterateAssumeFirstIteration |
| | 214 | .dirname_len = self.name_buffer.items.len - 1, |
| | 215 | }); |
| | 216 | top = &self.stack.items[self.stack.items.len - 1]; |
| | 217 | containing = &self.stack.items[self.stack.items.len - 2]; |
| | 218 | } |
| | 219 | } |
| | 220 | return .{ |
| | 221 | .dir = containing.iter.dir, |
| | 222 | .basename = self.name_buffer.items[dirname_len .. self.name_buffer.items.len - 1 :0], |
| | 223 | .path = self.name_buffer.items[0 .. self.name_buffer.items.len - 1 :0], |
| | 224 | .type = base.type, |
| | 225 | }; |
| | 226 | } else { |
| | 227 | var item = self.stack.pop().?; |
| | 228 | if (self.stack.items.len != 0) item.iter.dir.close(); |
| | 229 | } |
| | 230 | } |
| | 231 | return null; |
| | 232 | } |
| | 233 | |
| | 234 | pub fn deinit(self: *Walker) void { |
| | 235 | const gpa = self.allocator; |
| | 236 | // Close any remaining directories except the initial one (which is always at index 0) |
| | 237 | if (self.stack.items.len > 1) { |
| | 238 | for (self.stack.items[1..]) |*item| { |
| | 239 | item.iter.dir.close(); |
| | 240 | } |
| | 241 | } |
| | 242 | self.stack.deinit(gpa); |
| | 243 | self.name_buffer.deinit(gpa); |
| | 244 | } |
| | 245 | }; |
| | 246 | |
| 154 | pub fn rename(self: Dir, old: [:0]const u8, new: [:0]const u8) !void { | 247 | pub fn rename(self: Dir, old: [:0]const u8, new: [:0]const u8) !void { |
| 155 | return sys.renameat(@intFromEnum(self.fd), old.ptr, @intFromEnum(self.fd), new.ptr); | 248 | return sys.renameat(@intFromEnum(self.fd), old.ptr, @intFromEnum(self.fd), new.ptr); |
| 156 | } | 249 | } |