summaryrefslogtreecommitdiff
path: root/src/fs.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/fs.zig')
-rw-r--r--src/fs.zig57
1 files changed, 57 insertions, 0 deletions
diff --git a/src/fs.zig b/src/fs.zig
index 14b6a76a..50b56f76 100644
--- a/src/fs.zig
+++ b/src/fs.zig
@@ -1200,6 +1200,63 @@ pub fn readFile(gpa: std.mem.Allocator, path: []const u8) ![]u8 {
return readFileLimit(gpa, path, limits.max_file_bytes);
}
+/// Restore first tries a relative argument under the default dump directory,
+/// then preserves the ordinary file lookup. Absolute paths remain verbatim.
+pub fn readRestore(gpa: std.mem.Allocator, path: []const u8) ![]u8 {
+ var dir_buf: [4096]u8 = undefined;
+ const dir = @import("dump.zig").defaultDirectory(&dir_buf);
+ return readRestoreIn(gpa, path, dir);
+}
+
+fn readRestoreIn(gpa: std.mem.Allocator, path: []const u8, dir: ?[]const u8) ![]u8 {
+ if (!std.fs.path.isAbsolute(path)) {
+ if (dir) |base| {
+ var buf: [4096]u8 = undefined;
+ if (std.fmt.bufPrint(&buf, "{s}/{s}", .{ base, path })) |candidate| {
+ if (readFile(gpa, candidate)) |bytes| return bytes else |err| switch (err) {
+ error.FileNotFound => {},
+ else => return err,
+ }
+ } else |_| {}
+ }
+ }
+ return readFile(gpa, path);
+}
+
+test "Restore prefers default directory then falls back to original path" {
+ if (!platform_has_fs) return error.SkipZigTest;
+ const gpa = std.testing.allocator;
+ var tmp = std.testing.tmpDir(.{});
+ defer tmp.cleanup();
+ const relative = try std.fmt.allocPrint(gpa, ".zig-cache/tmp/{s}/a.dump", .{tmp.sub_path});
+ defer gpa.free(relative);
+ const base = try std.fmt.allocPrint(gpa, ".zig-cache/tmp/{s}/dumps", .{tmp.sub_path});
+ defer gpa.free(base);
+ const nested = try std.fmt.allocPrint(gpa, "dumps/.zig-cache/tmp/{s}", .{tmp.sub_path});
+ defer gpa.free(nested);
+ try tmp.dir.createDirPath(std.testing.io, nested);
+ try tmp.dir.writeFile(std.testing.io, .{ .sub_path = "dumps/a.dump", .data = "short name" });
+ const short = try readRestoreIn(gpa, "a.dump", base);
+ defer gpa.free(short);
+ try std.testing.expectEqualStrings("short name", short);
+ try tmp.dir.writeFile(std.testing.io, .{ .sub_path = "a.dump", .data = "original" });
+ const fallback = try readRestoreIn(gpa, relative, base);
+ defer gpa.free(fallback);
+ try std.testing.expectEqualStrings("original", fallback);
+ const candidate = try std.fmt.allocPrint(gpa, "{s}/{s}", .{ base, relative });
+ defer gpa.free(candidate);
+ try writeFile(candidate, "default");
+ const preferred = try readRestoreIn(gpa, relative, base);
+ defer gpa.free(preferred);
+ try std.testing.expectEqualStrings("default", preferred);
+ const absolute = try tmp.dir.realPathFileAlloc(std.testing.io, "a.dump", gpa);
+ defer gpa.free(absolute);
+ const direct = try readRestoreIn(gpa, absolute, base);
+ defer gpa.free(direct);
+ try std.testing.expectEqualStrings("original", direct);
+ try std.testing.expectError(error.FileNotFound, readRestoreIn(gpa, "missing-restore-test.dump", base));
+}
+
fn readFileLimit(gpa: std.mem.Allocator, path: []const u8, limit: usize) ![]u8 {
if (std.mem.indexOfScalar(u8, path, 0) != null) return error.OpenFailed;
if (!platform_has_fs or std.mem.startsWith(u8, path, "/virtual/")) {