A basic/naive approach (with no special handling of the dest existing, metadata copying, etc) would be:
const std = @import("std");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer std.debug.assert(gpa.deinit() == .ok);
const allocator = gpa.allocator();
var src_dir = try std.fs.cwd().openIterableDir("src_dir", .{});
defer src_dir.close();
var dest_dir = try std.fs.cwd().makeOpenPath("dest_dir", .{});
defer dest_dir.close();
var walker = try src_dir.walk(allocator);
defer walker.deinit();
while (try walker.next()) |entry| {
switch (entry.kind) {
.file => {
try entry.dir.copyFile(entry.basename, dest_dir, entry.path, .{});
},
.directory => {
try dest_dir.makeDir(entry.path);
},
else => return error.UnexpectedEntryKind,
}
}
}
Out of curiosity, I benchmarked it on Windows against xcopy using a giant nested directory (the result of npx create-react-app my-app) and the Zig version loses, so there’s potential for optimization:
> hyperfine "xcopy /E /Q /I /Y my-app my-app-copy" "copydir.exe" --prepare="rm -rf my-app-copy"
Benchmark 1: xcopy /E /Q /I /Y my-app my-app-copy
Time (mean ± σ): 23.462 s ± 0.625 s [User: 1.557 s, System: 19.337 s]
Range (min … max): 22.704 s … 24.793 s 10 runs
Benchmark 2: copydir.exe
Time (mean ± σ): 27.017 s ± 0.307 s [User: 1.063 s, System: 24.863 s]
Range (min … max): 26.502 s … 27.508 s 10 runs
Summary
'xcopy /E /Q /I /Y my-app my-app-copy' ran
1.15 ± 0.03 times faster than 'copydir.exe'
(similar results with the c_allocator, so it’s not the GPA that’s causing the difference)