zig-extras icon indicating copy to clipboard operation
zig-extras copied to clipboard

reverse file search: Count Steps to Directory With Entry as diff file

Open matu3ba opened this issue 2 years ago • 1 comments
trafficstars

This can be useful, if one does filter rules during traversal instead of mem.eql. I created this on writing the algo for searching upwards build.zig before realizing that comparing against the the search string is much simpler.

diff --git a/lib/std/fs.zig b/lib/std/fs.zig
index 31354a278..f06db4943 100644
--- a/lib/std/fs.zig
+++ b/lib/std/fs.zig
@@ -2085,6 +2085,44 @@ pub const Dir = struct {
         }
     }
 
+    pub const CountStepsToFolderWithEntryError = error{
+        InvalidKind,
+        SearchedFileNotFound,
+    } || std.fs.Dir.Iterator.Error || std.fs.Dir.OpenError;
+
+    /// Traverse folders upwards upwards until finding an entry with content `match`
+    /// Returns number of upwards traversals.
+    /// 0 indicates that the file is in the current directory
+    /// assert: dir was opened with iterate permissions
+    pub fn countStepsToFolderWithEntry(
+        self: Dir,
+        match: []const u8,
+        kind: File.Kind,
+    ) CountStepsToFolderWithEntryError!u32 {
+        var cnt_up: u32 = 0;
+        var diriter: Dir = undefined;
+        var dirparent: Dir = self;
+        while (true) {
+            diriter = dirparent;
+            defer diriter.close();
+            var iter = diriter.iterate();
+            while (try iter.next()) |entry| {
+                if (std.mem.eql(u8, entry.name, match)) {
+                    if (entry.kind != kind)
+                        return error.InvalidKind;
+                    return cnt_up;
+                }
+            }
+            dirparent = diriter.openDir("..", .{ .no_follow = true, .iterate = true }) catch |err| {
+                switch (err) {
+                    error.AccessDenied => return error.SearchedFileNotFound,
+                    else => return err,
+                }
+            };
+            cnt_up += 1;
+        }
+    }
+
     /// Writes content to the file system, creating a new file if it does not exist, truncating
     /// if it already exists.
     pub fn writeFile(self: Dir, sub_path: []const u8, data: []const u8) !void {
diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig
index f4003c180..35dbdd57e 100644
--- a/lib/std/fs/test.zig
+++ b/lib/std/fs/test.zig
@@ -1221,3 +1221,16 @@ test "File.PermissionsUnix" {
     try testing.expect(permissions_unix.unixHas(.user, .execute));
     try testing.expect(!permissions_unix.unixHas(.other, .execute));
 }
+
+test "Dir.countStepsToFolderWithEntry" {
+    const build_zig = "build.zig";
+    var tmp = std.testing.tmpDir(.{ .no_follow = true, .iterate = true });
+    defer tmp.cleanup();
+    _ = try tmp.dir.createFile(build_zig, .{});
+    var subdir = try tmp.dir.makeOpenPath("sub/sub/sub/sub/sub", .{
+        .no_follow = true,
+        .iterate = true,
+    });
+    const lvlups = try subdir.countStepsToFolderWithEntry(build_zig, .File);
+    try testing.expect(lvlups == @as(u32, 5));
+}

matu3ba avatar Jan 22 '23 02:01 matu3ba

Please close, if you think not useful/not fitting into collection.

matu3ba avatar Jan 22 '23 02:01 matu3ba