authorgravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-04-11 23:51:33 -07:00
committergravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-04-11 23:51:33 -07:00
log4b4f541419f9f655f38230504086e547667689e7
tree1604c71d2946a951e79751a607af2943ddead97a
parent57c6f626baf174dc4f7d3960ec45d9c9aaaf96f1
signaturebadge-check Signed by SSH key SHA256:4hHJbtBRU58AYXwjL7fkz2fnQHdiye8x1QpTCQ0sHNw

add Dir Walker


1 files changed, 93 insertions(+), 0 deletions(-)

Dir.zig+93
...@@ -151,6 +151,99 @@ pub const Iterator = struct {...@@ -151,6 +151,99 @@ pub const Iterator = struct {
151 };151 };
152};152};
153153
154pub 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
169pub 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
154pub fn rename(self: Dir, old: [:0]const u8, new: [:0]const u8) !void {247pub 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}