but switch is still being evaluated despite continue
total 447
drwxr-xr-x 6 charlie charlie 14B Sep 19 14:32 .
drwxr-xr-x 41 charlie charlie 81B Sep 19 14:33 ..
-rw-r--r-- 1 charlie charlie 27B Sep 19 14:32 .bashrc
-rw-r--r-- 1 charlie charlie 377K Sep 19 14:32 .face.icon
drwxr-xr-x 7 charlie charlie 12B Sep 19 14:32 .git
drwxr-xr-x 3 charlie charlie 4B Sep 19 14:32 .github
drwxr-xr-x 2 charlie charlie 3B Sep 19 14:32 .gnupg
walk recursively iterates through all elements of a folders, that includes files in subdirectories.
You are ignoring the folder named .git but you are still iterating through the contents of these folders.
Your Util.isIgnored will return false for e.g. .git/config, and so the switch gets executed for it, which I assume leads to the recreation of the .git folder later in the code.
walk: while (try walker.next()) |entry| {
if (Util.isIgnored(entry.basename, ignore_items)) {
if (entry.kind = .directory) {
// remove from stack, with prejudice
var item = walker.stack.pop().?;
// note: don't let this be the root directory
item.iter.dir.close();
}
continue :walk;
}
switch (entry.kind) {
.file => {
const src_path = try std.fs.path.join(
allocator,
&.{ source_with_slash, entry.path },
);
const dest_path = try std.fs.path.join(
allocator,
&.{ dest_with_slash, entry.path },
);
const file = Dotfile.new(src_path, dest_path);
try files.append(allocator, file);
},
else => continue :walk,
}
}
I haven’t tried this code but that’s the basic idea.
It’s pretty ugly stuff though, it would be better to encapsulate in a function like walker.skipThisDirectory(). Seems like skipping directories during a recursive iteration is the kind of thing which will happen.
total 448
drwxr-xr-x 7 charlie charlie 15B Sep 19 15:21 .
drwxr-xr-x 41 charlie charlie 81B Sep 19 15:21 ..
-rw-r--r-- 1 charlie charlie 27B Sep 19 15:21 .bashrc
drwxr-xr-x 33 charlie charlie 36B Sep 19 15:21 .config
-rw-r--r-- 1 charlie charlie 377K Sep 19 15:21 .face.icon
drwxr-xr-x 2 charlie charlie 3B Sep 19 15:21 .gnupg
-rw-r--r-- 1 charlie charlie 435B Sep 19 15:21 .gtkrc-2.0.mine
-rw-r--r-- 1 charlie charlie 402B Sep 19 15:21 .profile
-rw-r--r-- 1 charlie charlie 118B Sep 19 15:21 .shrc
-rw-r--r-- 1 charlie charlie 57B Sep 19 15:21 .xonshrc
-rw-r--r-- 1 charlie charlie 101B Sep 19 15:21 .zprofile
drwxr-xr-x 4 charlie charlie 4B Sep 19 15:21 .zsh-custom
-rw-r--r-- 1 charlie charlie 268B Sep 19 15:21 .zshrc
drwxr-xr-x 2 charlie charlie 13B Sep 19 15:21 bin
drwxr-xr-x 3 charlie charlie 4B Sep 19 15:21 misc
maybe I should open a PR for the Walker, feels like this is a very elaborate approach for a simple skip task.
AFAICT your only use of realpath is to convert paths to absolute, but there’s no need for that. The std.fs.Dir functions all work with any path type (you can use std.fs.cwd() to get a Dir to start with if you need it).
The cwd() approach will not work if the user is calling the application from the parent dir unrelated to relative paths in the app config. And my goal is to support both relative and absolute paths in the config (in some cases an absolute path in the config is dangerous). It works for me right now because I only handle relative paths in test configs. But things will change soon.
~/Programming/zig/tmp$ ./foorealpath
/home/ryan/Programming/zig/tmp/foo
~/Programming/zig/tmp$ cd subdir
~/Programming/zig/tmp/subdir$ ../foorealpath
/home/ryan/Programming/zig/tmp/subdir/foo
It sounds like you might want to use std.fs.selfExeDirPath as the starting point when resolving the paths:
const std = @import("std");
pub fn main() !void {
var debug_allocator = std.heap.DebugAllocator(.{}){};
defer std.debug.assert(debug_allocator.deinit() == .ok);
const allocator = debug_allocator.allocator();
const self_dir_path = try std.fs.selfExeDirPathAlloc(allocator);
defer allocator.free(self_dir_path);
std.debug.print("self dir path: {s}\n", .{self_dir_path});
var root_dir = try std.fs.cwd().openDir(self_dir_path, .{});
defer root_dir.close();
// Always will open the `foo` relative to the exe location
var some_file = try root_dir.openFile("foo", .{});
defer some_file.close();
}
~/Programming/zig/tmp$ ./fooselfexedir
self dir path: /home/ryan/Programming/zig/tmp
~/Programming/zig/tmp$ cd subdir
~/Programming/zig/tmp/subdir$ ../fooselfexedir
self dir path: /home/ryan/Programming/zig/tmp
oh wow, good to know, thanks!
tho not sure exe path can help on this one, common install locations like opt and etc. are just as unrelated to homedir as cwd might be.
I saw that one while thinking about ~ expansion. maybe ill just support paths with env vars instead (like $HOME/foo). That way I will have a concrete root directory as a starting point.
Since you mentioned my custom walker, I just want to give an update: I ended up rewriting it in a way which is the opposite of the default (and thus I have the opposite to skip). Instead of skipping directories when I notice I didn’t need to open them, I added an accept() function to traverse into the directory only when I need to. Might be a silly micro-optimization, but reduced openat and close syscalls by about 10%.
This is also a bit more in-line with the unmanaged data structures since I’m always calling it in the same location where I have the allocator, so I don’t need to store it in the walker struct.