file_system: add rename

This commit is contained in:
2026-06-19 13:40:50 +02:00
parent 3a0eab16a7
commit 89e5fff8a4
+89 -45
View File
@@ -95,6 +95,18 @@ const Entity = struct {
const removed = self.children.swapRemove(index); const removed = self.children.swapRemove(index);
removed.addr.deref(gpa); removed.addr.deref(gpa);
} }
fn contains(self: *Entity, maybe_descendant: *Entity) bool {
if (self == maybe_descendant)
return true;
if (!self.is_dir)
return false;
for (self.children.items) |child| {
if (child.addr.contains(maybe_descendant))
return true;
}
return false;
}
}; };
pub const OpenDir = struct { pub const OpenDir = struct {
@@ -153,6 +165,15 @@ pub const DeleteDirError = DeleteError || error {
DirNotEmpty, DirNotEmpty,
}; };
pub const RenameError = ResolveParentError || error{
NotFound,
IsDirectory,
NotDirectory,
DirNotEmpty,
OutOfMemory,
AccessDenied,
};
pub const OpenError = error{ pub const OpenError = error{
IsDirectory, IsDirectory,
NotDirectory, NotDirectory,
@@ -178,7 +199,6 @@ pub fn deinit(self: *FileSystem, gpa: Allocator) void {
} }
fn parsePath(path: []const u8, buffer: [][]const u8) ParsePathError![]const []const u8 { fn parsePath(path: []const u8, buffer: [][]const u8) ParsePathError![]const []const u8 {
var count: usize = 0; var count: usize = 0;
var cursor: usize = 0; var cursor: usize = 0;
@@ -192,7 +212,6 @@ fn parsePath(path: []const u8, buffer: [][]const u8) ParsePathError![]const []co
} }
while (cursor < path.len) { while (cursor < path.len) {
const start = cursor; const start = cursor;
while (cursor < path.len and path[cursor] != '/') while (cursor < path.len and path[cursor] != '/')
cursor += 1; cursor += 1;
@@ -220,13 +239,7 @@ fn parsePath(path: []const u8, buffer: [][]const u8) ParsePathError![]const []co
return buffer[0..count]; return buffer[0..count];
} }
fn resolvePath( fn resolvePath(self: *FileSystem, components: []const []const u8, root: ?*Entity, buffer: []*Entity) ResolvePathError![]const *Entity {
self : *FileSystem,
components: []const []const u8,
root : ?*Entity,
buffer : []*Entity
) ResolvePathError![]const *Entity {
var count: usize = 1; var count: usize = 1;
buffer[0] = root orelse self.root; buffer[0] = root orelse self.root;
@@ -263,7 +276,6 @@ fn resolveParent(self: *FileSystem, path: []const u8, root: ?*Entity) !ResolvePa
} }
fn createAny(self: *FileSystem, path: []const u8, root_dir: ?*OpenDir, is_dir: bool, gpa: Allocator) CreateError!void { fn createAny(self: *FileSystem, path: []const u8, root_dir: ?*OpenDir, is_dir: bool, gpa: Allocator) CreateError!void {
const result = try self.resolveParent(path, if (root_dir) |r| r.entity else null); const result = try self.resolveParent(path, if (root_dir) |r| r.entity else null);
const entity = try if (is_dir) Entity.initDir(gpa) else Entity.initFile(gpa); const entity = try if (is_dir) Entity.initDir(gpa) else Entity.initFile(gpa);
@@ -298,13 +310,69 @@ pub fn deleteDir(self: *FileSystem, path: []const u8, root_dir: ?*OpenDir, gpa:
try result.parent.removeChild(gpa, result.basename); try result.parent.removeChild(gpa, result.basename);
} }
fn openAny( pub fn rename(
self: *FileSystem, self: *FileSystem,
path : []const u8, old_path: []const u8,
root_dir : ?*OpenDir, old_root_dir: ?*OpenDir,
open_dir : *OpenDir, new_path: []const u8,
open_file: *OpenFile new_root_dir: ?*OpenDir,
) !bool { gpa: Allocator,
) RenameError!void {
const old = try self.resolveParent(old_path, if (old_root_dir) |r| r.entity else null);
const new = try self.resolveParent(new_path, if (new_root_dir) |r| r.entity else null);
const old_index = old.parent.findChildIndex(old.basename) orelse return RenameError.NotFound;
const old_child = old.parent.children.items[old_index];
if (old.parent == new.parent and std.mem.eql(u8, old.basename, new.basename))
return;
if (new.basename.len > ENTITY_NAME_LIMIT)
return RenameError.OutOfMemory;
if (old_child.addr.is_dir and old_child.addr.contains(new.parent))
return RenameError.AccessDenied;
const new_index_maybe = new.parent.findChildIndex(new.basename);
if (new_index_maybe) |new_index| {
const new_child = new.parent.children.items[new_index];
if (old_child.addr.is_dir and !new_child.addr.is_dir)
return RenameError.NotDirectory;
if (!old_child.addr.is_dir and new_child.addr.is_dir)
return RenameError.IsDirectory;
if (new_child.addr.is_dir and new_child.addr.children.items.len != 0)
return RenameError.DirNotEmpty;
}
if (old.parent == new.parent) {
if (new_index_maybe) |new_index| {
const removed = old.parent.children.swapRemove(new_index);
removed.addr.deref(gpa);
}
const moved = old.parent.findChild(old.basename) orelse return RenameError.NotFound;
moved.setName(new.basename) catch return RenameError.OutOfMemory;
return;
}
if (new_index_maybe == null)
try new.parent.children.ensureUnusedCapacity(gpa, 1);
old_child.addr.ref();
defer old_child.addr.deref(gpa);
if (new_index_maybe) |new_index| {
const removed = new.parent.children.swapRemove(new_index);
removed.addr.deref(gpa);
}
try old.parent.removeChild(gpa, old.basename);
new.parent.addChild(gpa, new.basename, old_child.addr) catch |err| switch (err) {
error.ExistsAlready => unreachable,
error.OutOfMemory => return error.OutOfMemory,
};
}
fn openAny(self: *FileSystem, path: []const u8, root_dir: ?*OpenDir, open_dir: *OpenDir, open_file: *OpenFile) !bool {
var component_buffer: [MAX_PATH_COMPONENTS][]const u8 = undefined; var component_buffer: [MAX_PATH_COMPONENTS][]const u8 = undefined;
var resolve_buffer: [MAX_PATH_COMPONENTS]*Entity = undefined; var resolve_buffer: [MAX_PATH_COMPONENTS]*Entity = undefined;
@@ -326,13 +394,7 @@ fn openAny(
return entity.is_dir; return entity.is_dir;
} }
pub fn openDir( pub fn openDir(self: *FileSystem, path: []const u8, root_dir: ?*OpenDir, open_dir: *OpenDir) OpenError!void {
self : *FileSystem,
path : []const u8,
root_dir: ?*OpenDir,
open_dir: *OpenDir
) OpenError!void {
var dummy: OpenFile = undefined; var dummy: OpenFile = undefined;
const is_dir = try self.openAny(path, root_dir, open_dir, &dummy); const is_dir = try self.openAny(path, root_dir, open_dir, &dummy);
if (!is_dir) { if (!is_dir) {
@@ -341,13 +403,7 @@ pub fn openDir(
open_dir.entity.ref(); open_dir.entity.ref();
} }
pub fn openFile( pub fn openFile(self: *FileSystem, path: []const u8, root_dir: ?*OpenDir, open_file: *OpenFile) OpenError!void {
self : *FileSystem,
path : []const u8,
root_dir : ?*OpenDir,
open_file: *OpenFile
) OpenError!void {
var dummy: OpenDir = undefined; var dummy: OpenDir = undefined;
const is_dir = try self.openAny(path, root_dir, &dummy, open_file); const is_dir = try self.openAny(path, root_dir, &dummy, open_file);
if (is_dir) { if (is_dir) {
@@ -371,18 +427,12 @@ pub fn readDir(self: *FileSystem, open_dir: *OpenDir) ReadDirError!ReadDir {
if (open_dir.cursor == 0) { if (open_dir.cursor == 0) {
open_dir.cursor += 1; open_dir.cursor += 1;
return .{ return .{ .name = ".", .is_dir = true };
.name = ".",
.is_dir = true
};
} }
if (open_dir.cursor == 1) { if (open_dir.cursor == 1) {
open_dir.cursor += 1; open_dir.cursor += 1;
return .{ return .{ .name = "..", .is_dir = true };
.name = "..",
.is_dir = true
};
} }
const index = open_dir.cursor - 2; const index = open_dir.cursor - 2;
@@ -416,13 +466,7 @@ pub fn readFile(self: *FileSystem, open_file: *OpenFile, offset_maybe: ?usize, t
return num; return num;
} }
pub fn writeFile( pub fn writeFile(self: *FileSystem, open_file: *OpenFile, gpa: Allocator, offset_maybe: ?usize, source: []const u8) Allocator.Error!void {
self : *FileSystem,
open_file : *OpenFile,
gpa : Allocator,
offset_maybe: ?usize,
source : []const u8
) Allocator.Error!void {
_ = self; _ = self;
const offset = offset_maybe orelse open_file.cursor; const offset = offset_maybe orelse open_file.cursor;