aboutsummaryrefslogtreecommitdiff
path: root/src/walker_skippable.zig
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2021-10-14 18:55:41 -0700
committerGravatar Jarred Sumner <jarred@jarredsumner.com> 2021-10-14 18:55:41 -0700
commitbbc1bcbed125e4aeacac0c374f717f65adb838ea (patch)
treea3ae72a500afc507231d3f97c7d0762c76614a51 /src/walker_skippable.zig
parent3ed824fe0fc14d21a5c035d84891b8ecf28e3c44 (diff)
downloadbun-bbc1bcbed125e4aeacac0c374f717f65adb838ea.tar.gz
bun-bbc1bcbed125e4aeacac0c374f717f65adb838ea.tar.zst
bun-bbc1bcbed125e4aeacac0c374f717f65adb838ea.zip
Support local templates
Diffstat (limited to '')
-rw-r--r--src/walker_skippable.zig147
1 files changed, 147 insertions, 0 deletions
diff --git a/src/walker_skippable.zig b/src/walker_skippable.zig
new file mode 100644
index 000000000..809901bc0
--- /dev/null
+++ b/src/walker_skippable.zig
@@ -0,0 +1,147 @@
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+const Walker = @This();
+const path = std.fs.path;
+
+stack: std.ArrayList(StackItem),
+name_buffer: std.ArrayList(u8),
+skip_filenames: []const u64 = &[_]u64{},
+skip_dirnames: []const u64 = &[_]u64{},
+skip_all: []const u64 = &[_]u64{},
+seed: u64 = 0,
+
+const Dir = std.fs.Dir;
+
+pub const WalkerEntry = struct {
+ /// The containing directory. This can be used to operate directly on `basename`
+ /// rather than `path`, avoiding `error.NameTooLong` for deeply nested paths.
+ /// The directory remains open until `next` or `deinit` is called.
+ dir: Dir,
+ basename: []const u8,
+ path: []const u8,
+ kind: Dir.Entry.Kind,
+};
+
+const StackItem = struct {
+ iter: Dir.Iterator,
+ dirname_len: usize,
+};
+
+/// After each call to this function, and on deinit(), the memory returned
+/// from this function becomes invalid. A copy must be made in order to keep
+/// a reference to the path.
+pub fn next(self: *Walker) !?WalkerEntry {
+ while (self.stack.items.len != 0) {
+ // `top` becomes invalid after appending to `self.stack`
+ var top = &self.stack.items[self.stack.items.len - 1];
+ var dirname_len = top.dirname_len;
+ if (try top.iter.next()) |base| {
+ switch (base.kind) {
+ .Directory => {
+ if (std.mem.indexOfScalar(u64, self.skip_dirnames, std.hash.Wyhash.hash(self.seed, base.name)) != null) continue;
+ },
+ .File => {
+ if (std.mem.indexOfScalar(u64, self.skip_filenames, std.hash.Wyhash.hash(self.seed, base.name)) != null) continue;
+ },
+
+ // we don't know what it is for a symlink
+ .SymLink => {
+ if (std.mem.indexOfScalar(u64, self.skip_all, std.hash.Wyhash.hash(self.seed, base.name)) != null) continue;
+ },
+
+ else => {},
+ }
+
+ self.name_buffer.shrinkRetainingCapacity(dirname_len);
+ if (self.name_buffer.items.len != 0) {
+ try self.name_buffer.append(path.sep);
+ dirname_len += 1;
+ }
+ try self.name_buffer.appendSlice(base.name);
+ if (base.kind == .Directory) {
+ var new_dir = top.iter.dir.openDir(base.name, .{ .iterate = true }) catch |err| switch (err) {
+ error.NameTooLong => unreachable, // no path sep in base.name
+ else => |e| return e,
+ };
+ {
+ errdefer new_dir.close();
+ try self.stack.append(StackItem{
+ .iter = new_dir.iterate(),
+ .dirname_len = self.name_buffer.items.len,
+ });
+ top = &self.stack.items[self.stack.items.len - 1];
+ }
+ }
+ return WalkerEntry{
+ .dir = top.iter.dir,
+ .basename = self.name_buffer.items[dirname_len..],
+ .path = self.name_buffer.items,
+ .kind = base.kind,
+ };
+ } else {
+ var item = self.stack.pop();
+ if (self.stack.items.len != 0) {
+ item.iter.dir.close();
+ }
+ }
+ }
+ return null;
+}
+
+pub fn deinit(self: *Walker) void {
+ while (self.stack.popOrNull()) |*item| {
+ if (self.stack.items.len != 0) {
+ item.iter.dir.close();
+ }
+ }
+ self.stack.deinit();
+ self.name_buffer.allocator.free(self.skip_all);
+ self.name_buffer.deinit();
+}
+
+/// Recursively iterates over a directory.
+/// `self` must have been opened with `OpenDirOptions{.iterate = true}`.
+/// Must call `Walker.deinit` when done.
+/// The order of returned file system entries is undefined.
+/// `self` will not be closed after walking it.
+pub fn walk(
+ self: Dir,
+ allocator: *Allocator,
+ skip_filenames: []const []const u8,
+ skip_dirnames: []const []const u8,
+) !Walker {
+ var name_buffer = std.ArrayList(u8).init(allocator);
+ errdefer name_buffer.deinit();
+
+ var stack = std.ArrayList(Walker.StackItem).init(allocator);
+ errdefer stack.deinit();
+
+ var skip_names = try allocator.alloc(u64, skip_filenames.len + skip_dirnames.len);
+ const seed = skip_filenames.len + skip_dirnames.len;
+ var skip_name_i: usize = 0;
+
+ for (skip_filenames) |name| {
+ skip_names[skip_name_i] = std.hash.Wyhash.hash(seed, name);
+ skip_name_i += 1;
+ }
+ var skip_filenames_ = skip_names[0..skip_name_i];
+ var skip_dirnames_ = skip_names[skip_name_i..];
+
+ for (skip_dirnames) |name, i| {
+ skip_dirnames_[i] = std.hash.Wyhash.hash(seed, name);
+ }
+
+ try stack.append(Walker.StackItem{
+ .iter = self.iterate(),
+ .dirname_len = 0,
+ });
+
+ return Walker{
+ .stack = stack,
+ .name_buffer = name_buffer,
+ .skip_all = skip_names,
+ .seed = seed,
+ .skip_filenames = skip_filenames_,
+ .skip_dirnames = skip_dirnames_,
+ };
+}