mirror of
https://github.com/ziglang/zig.git
synced 2024-11-16 17:15:37 +00:00
Add rename to std.fs API
- Moves fs.rename functions to fs.renameAbsolute to match other functions outside of fs.Dir - Adds fs.Dir.rename that takes two paths relative to the given Dir - Adds fs.rename that takes two separate Dir's that the given paths are relative to (for renaming across directories without having to make the second path relative to a single directory) - Fixes FileNotFound error return in std.os.windows.MoveFileExW - Returns error.RenameAcrossMountPoints from renameatW + Matches the RenameAcrossMountPoints error return in renameatWasi/renameatZ
This commit is contained in:
parent
3672a18799
commit
5e3fa0e94f
@ -21,10 +21,6 @@ pub const wasi = @import("fs/wasi.zig");
|
||||
|
||||
// TODO audit these APIs with respect to Dir and absolute paths
|
||||
|
||||
pub const rename = os.rename;
|
||||
pub const renameZ = os.renameZ;
|
||||
pub const renameC = @compileError("deprecated: renamed to renameZ");
|
||||
pub const renameW = os.renameW;
|
||||
pub const realpath = os.realpath;
|
||||
pub const realpathZ = os.realpathZ;
|
||||
pub const realpathC = @compileError("deprecated: renamed to realpathZ");
|
||||
@ -90,7 +86,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:
|
||||
base64_encoder.encode(tmp_path[dirname.len + 1 ..], &rand_buf);
|
||||
|
||||
if (cwd().symLink(existing_path, tmp_path, .{})) {
|
||||
return rename(tmp_path, new_path);
|
||||
return cwd().rename(tmp_path, new_path);
|
||||
} else |err| switch (err) {
|
||||
error.PathAlreadyExists => continue,
|
||||
else => return err, // TODO zig should know this set does not include PathAlreadyExists
|
||||
@ -255,6 +251,45 @@ pub fn deleteDirAbsoluteW(dir_path: [*:0]const u16) !void {
|
||||
return os.rmdirW(dir_path);
|
||||
}
|
||||
|
||||
pub const renameC = @compileError("deprecated: use renameZ, dir.renameZ, or renameAbsoluteZ");
|
||||
|
||||
/// Same as `Dir.rename` except the paths are absolute.
|
||||
pub fn renameAbsolute(old_path: []const u8, new_path: []const u8) !void {
|
||||
assert(path.isAbsolute(old_path));
|
||||
assert(path.isAbsolute(new_path));
|
||||
return os.rename(old_path, new_path);
|
||||
}
|
||||
|
||||
/// Same as `renameAbsolute` except the path parameters are null-terminated.
|
||||
pub fn renameAbsoluteZ(old_path: [*:0]const u8, new_path: [*:0]const u8) !void {
|
||||
assert(path.isAbsoluteZ(old_path));
|
||||
assert(path.isAbsoluteZ(new_path));
|
||||
return os.renameZ(old_path, new_path);
|
||||
}
|
||||
|
||||
/// Same as `renameAbsolute` except the path parameters are WTF-16 and target OS is assumed Windows.
|
||||
pub fn renameAbsoluteW(old_path: [*:0]const u16, new_path: [*:0]const u16) !void {
|
||||
assert(path.isAbsoluteWindowsW(old_path));
|
||||
assert(path.isAbsoluteWindowsW(new_path));
|
||||
return os.renameW(old_path, new_path);
|
||||
}
|
||||
|
||||
/// Same as `Dir.rename`, except `new_sub_path` is relative to `new_dir`
|
||||
pub fn rename(old_dir: Dir, old_sub_path: []const u8, new_dir: Dir, new_sub_path: []const u8) !void {
|
||||
return os.renameat(old_dir.fd, old_sub_path, new_dir.fd, new_sub_path);
|
||||
}
|
||||
|
||||
/// Same as `rename` except the parameters are null-terminated.
|
||||
pub fn renameZ(old_dir: Dir, old_sub_path_z: [*:0]const u8, new_dir: Dir, new_sub_path_z: [*:0]const u8) !void {
|
||||
return os.renameatZ(old_dir.fd, old_sub_path_z, new_dir.fd, new_sub_path_z);
|
||||
}
|
||||
|
||||
/// Same as `rename` except the parameters are UTF16LE, NT prefixed.
|
||||
/// This function is Windows-only.
|
||||
pub fn renameW(old_dir: Dir, old_sub_path_w: []const u16, new_dir: Dir, new_sub_path_w: []const u16) !void {
|
||||
return os.renameatW(old_dir.fd, old_sub_path_w, new_dir.fd, new_sub_path_w);
|
||||
}
|
||||
|
||||
pub const Dir = struct {
|
||||
fd: os.fd_t,
|
||||
|
||||
@ -1338,6 +1373,27 @@ pub const Dir = struct {
|
||||
};
|
||||
}
|
||||
|
||||
pub const RenameError = os.RenameError;
|
||||
|
||||
/// Change the name or location of a file or directory.
|
||||
/// If new_sub_path already exists, it will be replaced.
|
||||
/// Renaming a file over an existing directory or a directory
|
||||
/// over an existing file will fail with `error.IsDir` or `error.NotDir`
|
||||
pub fn rename(self: Dir, old_sub_path: []const u8, new_sub_path: []const u8) RenameError!void {
|
||||
return os.renameat(self.fd, old_sub_path, self.fd, new_sub_path);
|
||||
}
|
||||
|
||||
/// Same as `rename` except the parameters are null-terminated.
|
||||
pub fn renameZ(self: Dir, old_sub_path_z: [*:0]const u8, new_sub_path_z: [*:0]const u8) RenameError!void {
|
||||
return os.renameatZ(self.fd, old_sub_path_z, self.fd, new_sub_path_z);
|
||||
}
|
||||
|
||||
/// Same as `rename` except the parameters are UTF16LE, NT prefixed.
|
||||
/// This function is Windows-only.
|
||||
pub fn renameW(self: Dir, old_sub_path_w: []const u16, new_sub_path_w: []const u16) RenameError!void {
|
||||
return os.renameatW(self.fd, old_sub_path_w, self.fd, new_sub_path_w);
|
||||
}
|
||||
|
||||
/// Creates a symbolic link named `sym_link_path` which contains the string `target_path`.
|
||||
/// A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent
|
||||
/// one; the latter case is known as a dangling link.
|
||||
|
@ -274,6 +274,167 @@ test "file operations on directories" {
|
||||
dir.close();
|
||||
}
|
||||
|
||||
test "Dir.rename files" {
|
||||
var tmp_dir = tmpDir(.{});
|
||||
defer tmp_dir.cleanup();
|
||||
|
||||
testing.expectError(error.FileNotFound, tmp_dir.dir.rename("missing_file_name", "something_else"));
|
||||
|
||||
// Renaming files
|
||||
const test_file_name = "test_file";
|
||||
const renamed_test_file_name = "test_file_renamed";
|
||||
var file = try tmp_dir.dir.createFile(test_file_name, .{ .read = true });
|
||||
file.close();
|
||||
try tmp_dir.dir.rename(test_file_name, renamed_test_file_name);
|
||||
|
||||
// Ensure the file was renamed
|
||||
testing.expectError(error.FileNotFound, tmp_dir.dir.openFile(test_file_name, .{}));
|
||||
file = try tmp_dir.dir.openFile(renamed_test_file_name, .{});
|
||||
file.close();
|
||||
|
||||
// Rename to self succeeds
|
||||
try tmp_dir.dir.rename(renamed_test_file_name, renamed_test_file_name);
|
||||
|
||||
// Rename to existing file succeeds
|
||||
var existing_file = try tmp_dir.dir.createFile("existing_file", .{ .read = true });
|
||||
existing_file.close();
|
||||
try tmp_dir.dir.rename(renamed_test_file_name, "existing_file");
|
||||
|
||||
testing.expectError(error.FileNotFound, tmp_dir.dir.openFile(renamed_test_file_name, .{}));
|
||||
file = try tmp_dir.dir.openFile("existing_file", .{});
|
||||
file.close();
|
||||
}
|
||||
|
||||
test "Dir.rename directories" {
|
||||
// TODO: Fix on Windows, see https://github.com/ziglang/zig/issues/6364
|
||||
if (builtin.os.tag == .windows) return error.SkipZigTest;
|
||||
|
||||
var tmp_dir = tmpDir(.{});
|
||||
defer tmp_dir.cleanup();
|
||||
|
||||
// Renaming directories
|
||||
try tmp_dir.dir.makeDir("test_dir");
|
||||
try tmp_dir.dir.rename("test_dir", "test_dir_renamed");
|
||||
|
||||
// Ensure the directory was renamed
|
||||
testing.expectError(error.FileNotFound, tmp_dir.dir.openDir("test_dir", .{}));
|
||||
var dir = try tmp_dir.dir.openDir("test_dir_renamed", .{});
|
||||
|
||||
// Put a file in the directory
|
||||
var file = try dir.createFile("test_file", .{ .read = true });
|
||||
file.close();
|
||||
dir.close();
|
||||
|
||||
try tmp_dir.dir.rename("test_dir_renamed", "test_dir_renamed_again");
|
||||
|
||||
// Ensure the directory was renamed and the file still exists in it
|
||||
testing.expectError(error.FileNotFound, tmp_dir.dir.openDir("test_dir_renamed", .{}));
|
||||
dir = try tmp_dir.dir.openDir("test_dir_renamed_again", .{});
|
||||
file = try dir.openFile("test_file", .{});
|
||||
file.close();
|
||||
dir.close();
|
||||
|
||||
// Try to rename to a non-empty directory now
|
||||
var target_dir = try tmp_dir.dir.makeOpenPath("non_empty_target_dir", .{});
|
||||
file = try target_dir.createFile("filler", .{ .read = true });
|
||||
file.close();
|
||||
|
||||
testing.expectError(error.PathAlreadyExists, tmp_dir.dir.rename("test_dir_renamed_again", "non_empty_target_dir"));
|
||||
|
||||
// Ensure the directory was not renamed
|
||||
dir = try tmp_dir.dir.openDir("test_dir_renamed_again", .{});
|
||||
file = try dir.openFile("test_file", .{});
|
||||
file.close();
|
||||
dir.close();
|
||||
}
|
||||
|
||||
test "Dir.rename file <-> dir" {
|
||||
// TODO: Fix on Windows, see https://github.com/ziglang/zig/issues/6364
|
||||
if (builtin.os.tag == .windows) return error.SkipZigTest;
|
||||
|
||||
var tmp_dir = tmpDir(.{});
|
||||
defer tmp_dir.cleanup();
|
||||
|
||||
var file = try tmp_dir.dir.createFile("test_file", .{ .read = true });
|
||||
file.close();
|
||||
try tmp_dir.dir.makeDir("test_dir");
|
||||
testing.expectError(error.IsDir, tmp_dir.dir.rename("test_file", "test_dir"));
|
||||
testing.expectError(error.NotDir, tmp_dir.dir.rename("test_dir", "test_file"));
|
||||
}
|
||||
|
||||
test "rename" {
|
||||
var tmp_dir1 = tmpDir(.{});
|
||||
defer tmp_dir1.cleanup();
|
||||
|
||||
var tmp_dir2 = tmpDir(.{});
|
||||
defer tmp_dir2.cleanup();
|
||||
|
||||
// Renaming files
|
||||
const test_file_name = "test_file";
|
||||
const renamed_test_file_name = "test_file_renamed";
|
||||
var file = try tmp_dir1.dir.createFile(test_file_name, .{ .read = true });
|
||||
file.close();
|
||||
try fs.rename(tmp_dir1.dir, test_file_name, tmp_dir2.dir, renamed_test_file_name);
|
||||
|
||||
// ensure the file was renamed
|
||||
testing.expectError(error.FileNotFound, tmp_dir1.dir.openFile(test_file_name, .{}));
|
||||
file = try tmp_dir2.dir.openFile(renamed_test_file_name, .{});
|
||||
file.close();
|
||||
}
|
||||
|
||||
test "renameAbsolute" {
|
||||
if (builtin.os.tag == .wasi) return error.SkipZigTest;
|
||||
|
||||
var tmp_dir = tmpDir(.{});
|
||||
defer tmp_dir.cleanup();
|
||||
|
||||
// Get base abs path
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const allocator = &arena.allocator;
|
||||
|
||||
const base_path = blk: {
|
||||
const relative_path = try fs.path.join(&arena.allocator, &[_][]const u8{ "zig-cache", "tmp", tmp_dir.sub_path[0..] });
|
||||
break :blk try fs.realpathAlloc(&arena.allocator, relative_path);
|
||||
};
|
||||
|
||||
testing.expectError(error.FileNotFound, fs.renameAbsolute(
|
||||
try fs.path.join(allocator, &[_][]const u8{ base_path, "missing_file_name" }),
|
||||
try fs.path.join(allocator, &[_][]const u8{ base_path, "something_else" }),
|
||||
));
|
||||
|
||||
// Renaming files
|
||||
const test_file_name = "test_file";
|
||||
const renamed_test_file_name = "test_file_renamed";
|
||||
var file = try tmp_dir.dir.createFile(test_file_name, .{ .read = true });
|
||||
file.close();
|
||||
try fs.renameAbsolute(
|
||||
try fs.path.join(allocator, &[_][]const u8{ base_path, test_file_name }),
|
||||
try fs.path.join(allocator, &[_][]const u8{ base_path, renamed_test_file_name }),
|
||||
);
|
||||
|
||||
// ensure the file was renamed
|
||||
testing.expectError(error.FileNotFound, tmp_dir.dir.openFile(test_file_name, .{}));
|
||||
file = try tmp_dir.dir.openFile(renamed_test_file_name, .{});
|
||||
const stat = try file.stat();
|
||||
testing.expect(stat.kind == .File);
|
||||
file.close();
|
||||
|
||||
// Renaming directories
|
||||
const test_dir_name = "test_dir";
|
||||
const renamed_test_dir_name = "test_dir_renamed";
|
||||
try tmp_dir.dir.makeDir(test_dir_name);
|
||||
try fs.renameAbsolute(
|
||||
try fs.path.join(allocator, &[_][]const u8{ base_path, test_dir_name }),
|
||||
try fs.path.join(allocator, &[_][]const u8{ base_path, renamed_test_dir_name }),
|
||||
);
|
||||
|
||||
// ensure the directory was renamed
|
||||
testing.expectError(error.FileNotFound, tmp_dir.dir.openDir(test_dir_name, .{}));
|
||||
var dir = try tmp_dir.dir.openDir(renamed_test_dir_name, .{});
|
||||
dir.close();
|
||||
}
|
||||
|
||||
test "openSelfExe" {
|
||||
if (builtin.os.tag == .wasi) return error.SkipZigTest;
|
||||
|
||||
|
@ -1890,7 +1890,7 @@ pub fn unlinkatW(dirfd: fd_t, sub_path_w: []const u16, flags: u32) UnlinkatError
|
||||
return windows.DeleteFile(sub_path_w, .{ .dir = dirfd, .remove_dir = remove_dir });
|
||||
}
|
||||
|
||||
const RenameError = error{
|
||||
pub const RenameError = error{
|
||||
/// In WASI, this error may occur when the file descriptor does
|
||||
/// not hold the required rights to rename a resource by path relative to it.
|
||||
AccessDenied,
|
||||
@ -2107,6 +2107,7 @@ pub fn renameatW(
|
||||
.ACCESS_DENIED => return error.AccessDenied,
|
||||
.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
|
||||
.OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
|
||||
.NOT_SAME_DEVICE => error.RenameAcrossMountPoints,
|
||||
else => return windows.unexpectedStatus(rc),
|
||||
}
|
||||
}
|
||||
|
@ -828,7 +828,7 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil
|
||||
}
|
||||
}
|
||||
|
||||
pub const MoveFileError = error{Unexpected};
|
||||
pub const MoveFileError = error{ FileNotFound, Unexpected };
|
||||
|
||||
pub fn MoveFileEx(old_path: []const u8, new_path: []const u8, flags: DWORD) MoveFileError!void {
|
||||
const old_path_w = try sliceToPrefixedFileW(old_path);
|
||||
@ -839,6 +839,7 @@ pub fn MoveFileEx(old_path: []const u8, new_path: []const u8, flags: DWORD) Move
|
||||
pub fn MoveFileExW(old_path: [*:0]const u16, new_path: [*:0]const u16, flags: DWORD) MoveFileError!void {
|
||||
if (kernel32.MoveFileExW(old_path, new_path, flags) == 0) {
|
||||
switch (kernel32.GetLastError()) {
|
||||
.FILE_NOT_FOUND => return error.FileNotFound,
|
||||
else => |err| return unexpectedError(err),
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user