feat: Add file move operation
- Added `move` operation to `Directory` to rename files and directories within the same directory. This improves file management capabilities. - Updated `VFS` interface to include `move` function with FSEntry return type for consistency. This allows for retrieving metadata of the moved file/directory. - Implemented `move` operation for `LocalVFS`, `OurDBVFS`, and `NestedVFS`. This provides consistent file move functionality across different VFS implementations. - Added tests for the new move functionality in `vfsourdb_test.v`. This ensures the correct behavior of the new feature.
This commit is contained in:
@@ -240,6 +240,31 @@ pub fn (mut dir Directory) rm(name string) ! {
|
||||
dir.myvfs.save_entry(dir)!
|
||||
}
|
||||
|
||||
pub fn (mut dir Directory) move(src_name string, dst_name string) !FSEntry {
|
||||
mut found := false
|
||||
mut new_entry := FSEntry(dir)
|
||||
|
||||
for child_id in dir.children {
|
||||
if mut entry := dir.myvfs.load_entry(child_id) {
|
||||
if entry.metadata.name == src_name {
|
||||
found = true
|
||||
// Create a new directory entry with the new name
|
||||
entry.metadata.name = dst_name
|
||||
entry.metadata.modified_at = time.now().unix()
|
||||
dir.myvfs.save_entry(entry)!
|
||||
new_entry = entry
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
return error('${src_name} not found')
|
||||
}
|
||||
|
||||
return new_entry
|
||||
}
|
||||
|
||||
// get_children returns all immediate children as FSEntry objects
|
||||
pub fn (mut dir Directory) children(recursive bool) ![]FSEntry {
|
||||
mut entries := []FSEntry{}
|
||||
|
||||
@@ -7,12 +7,12 @@ import time
|
||||
@[heap]
|
||||
pub struct OurDBFS {
|
||||
pub mut:
|
||||
root_id u32 // ID of root directory
|
||||
block_size u32 // Size of data blocks in bytes
|
||||
data_dir string // Directory to store OurDBFS data
|
||||
metadata_dir string // Directory where we store the metadata
|
||||
db_data &ourdb.OurDB @[str: skip]// Database instance for persistent storage
|
||||
db_meta &ourdb.OurDB @[str: skip]// Database instance for metadata storage
|
||||
root_id u32 // ID of root directory
|
||||
block_size u32 // Size of data blocks in bytes
|
||||
data_dir string // Directory to store OurDBFS data
|
||||
metadata_dir string // Directory where we store the metadata
|
||||
db_data &ourdb.OurDB @[str: skip] // Database instance for persistent storage
|
||||
db_meta &ourdb.OurDB @[str: skip] // Database instance for metadata storage
|
||||
last_inserted_id u32
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ pub fn (mut fs OurDBFS) get_root() !&Directory {
|
||||
}
|
||||
|
||||
// load_entry loads an entry from the database by ID and sets up parent references
|
||||
fn (mut fs OurDBFS) load_entry(id u32) !FSEntry {
|
||||
pub fn (mut fs OurDBFS) load_entry(id u32) !FSEntry {
|
||||
if data := fs.db_meta.get(id) {
|
||||
// First byte is version, second byte indicates the type
|
||||
// TODO: check we dont overflow filetype (u8 in boundaries of filetype)
|
||||
|
||||
@@ -65,7 +65,7 @@ mut:
|
||||
get(path string) !FSEntry
|
||||
rename(old_path string, new_path string) !
|
||||
copy(src_path string, dst_path string) !
|
||||
move(src_path string, dst_path string) !
|
||||
move(src_path string, dst_path string) !FSEntry
|
||||
delete(path string) !
|
||||
|
||||
// Symlink operations
|
||||
|
||||
@@ -257,7 +257,7 @@ pub fn (myvfs LocalVFS) copy(src_path string, dst_path string) ! {
|
||||
os.cp(abs_src, abs_dst) or { return error('Failed to copy ${src_path} to ${dst_path}: ${err}') }
|
||||
}
|
||||
|
||||
pub fn (myvfs LocalVFS) move(src_path string, dst_path string) ! {
|
||||
pub fn (myvfs LocalVFS) move(src_path string, dst_path string) !FSEntry {
|
||||
abs_src := myvfs.abs_path(src_path)
|
||||
abs_dst := myvfs.abs_path(dst_path)
|
||||
|
||||
@@ -269,6 +269,13 @@ pub fn (myvfs LocalVFS) move(src_path string, dst_path string) ! {
|
||||
}
|
||||
|
||||
os.mv(abs_src, abs_dst) or { return error('Failed to move ${src_path} to ${dst_path}: ${err}') }
|
||||
metadata := myvfs.os_attr_to_metadata(abs_dst) or {
|
||||
return error('Failed to get metadata: ${err}')
|
||||
}
|
||||
return LocalFSEntry{
|
||||
path: dst_path
|
||||
metadata: metadata
|
||||
}
|
||||
}
|
||||
|
||||
// Generic delete operation that handles all types
|
||||
|
||||
@@ -168,19 +168,10 @@ pub fn (mut self NestedVFS) copy(src_path string, dst_path string) ! {
|
||||
return dst_impl.file_write(dst_rel_path, data)
|
||||
}
|
||||
|
||||
pub fn (mut self NestedVFS) move(src_path string, dst_path string) ! {
|
||||
pub fn (mut self NestedVFS) move(src_path string, dst_path string) !vfscore.FSEntry {
|
||||
mut src_impl, src_rel_path := self.find_vfs(src_path)!
|
||||
mut dst_impl, dst_rel_path := self.find_vfs(dst_path)!
|
||||
|
||||
if src_impl == dst_impl {
|
||||
return src_impl.move(src_rel_path, dst_rel_path)
|
||||
}
|
||||
|
||||
// Move across different VFS implementations
|
||||
// TODO: Q: What if it's not file? What if it's a symlink or directory?
|
||||
data := src_impl.file_read(src_rel_path)!
|
||||
dst_impl.file_create(dst_rel_path)!
|
||||
return dst_impl.file_write(dst_rel_path, data)
|
||||
_, dst_rel_path := self.find_vfs(dst_path)!
|
||||
return src_impl.move(src_rel_path, dst_rel_path)
|
||||
}
|
||||
|
||||
pub fn (mut self NestedVFS) link_create(target_path string, link_path string) !vfscore.FSEntry {
|
||||
@@ -238,7 +229,7 @@ fn (e &MountEntry) get_metadata() vfscore.Metadata {
|
||||
}
|
||||
|
||||
fn (e &MountEntry) get_path() string {
|
||||
return "/${e.metadata.name.trim_left('/')}"
|
||||
return '/${e.metadata.name.trim_left('/')}'
|
||||
}
|
||||
|
||||
// is_dir returns true if the entry is a directory
|
||||
|
||||
@@ -135,8 +135,14 @@ pub fn (mut self OurDBVFS) copy(src_path string, dst_path string) ! {
|
||||
return error('Not implemented')
|
||||
}
|
||||
|
||||
pub fn (mut self OurDBVFS) move(src_path string, dst_path string) ! {
|
||||
return error('Not implemented')
|
||||
pub fn (mut self OurDBVFS) move(src_path string, dst_path string) !vfscore.FSEntry {
|
||||
src_parent_path := os.dir(src_path)
|
||||
src_name := os.base(src_path)
|
||||
dst_name := os.base(dst_path)
|
||||
|
||||
mut src_parent_dir := self.get_directory(src_parent_path)!
|
||||
moved_dir := src_parent_dir.move(src_name, dst_name)!
|
||||
return convert_to_vfscore_entry(moved_dir)
|
||||
}
|
||||
|
||||
pub fn (mut self OurDBVFS) link_create(target_path string, link_path string) !vfscore.FSEntry {
|
||||
|
||||
@@ -42,35 +42,45 @@ fn test_vfsourdb() ! {
|
||||
read_content := vfs.file_read('/test_dir/test.txt')!
|
||||
assert read_content == test_content
|
||||
|
||||
// Test directory move
|
||||
moved_dir := vfs.move('/test_dir', '/test_dir2')!
|
||||
|
||||
assert moved_dir.get_metadata().name == 'test_dir2'
|
||||
assert moved_dir.get_metadata().file_type == .directory
|
||||
|
||||
assert vfs.exists('/test_dir') == false
|
||||
assert vfs.exists('/test_dir2/test.txt') == true
|
||||
|
||||
// Test directory listing
|
||||
mut entries := vfs.dir_list('/test_dir')!
|
||||
mut entries := vfs.dir_list('/test_dir2')!
|
||||
assert entries.len == 1
|
||||
assert entries[0].get_metadata().name == 'test.txt'
|
||||
|
||||
// Test exists
|
||||
assert vfs.exists('/test_dir') == true
|
||||
assert vfs.exists('/test_dir/test.txt') == true
|
||||
assert vfs.exists('/test_dir2') == true
|
||||
assert vfs.exists('/test_dir2/test.txt') == true
|
||||
assert vfs.exists('/nonexistent') == false
|
||||
|
||||
// Test symlink creation and reading
|
||||
vfs.link_create('/test_dir/test.txt', '/test_dir/test_link')!
|
||||
link_target := vfs.link_read('/test_dir/test_link')!
|
||||
assert link_target == '/test_dir/test.txt'
|
||||
vfs.link_create('/test_dir2/test.txt', '/test_dir2/test_link')!
|
||||
link_target := vfs.link_read('/test_dir2/test_link')!
|
||||
assert link_target == '/test_dir2/test.txt'
|
||||
|
||||
// Test symlink deletion
|
||||
vfs.link_delete('/test_dir/test_link')!
|
||||
assert vfs.exists('/test_dir/test_link') == false
|
||||
vfs.link_delete('/test_dir2/test_link')!
|
||||
assert vfs.exists('/test_dir2/test_link') == false
|
||||
|
||||
// Test file deletion
|
||||
vfs.file_delete('/test_dir/test.txt')!
|
||||
assert vfs.exists('/test_dir/test.txt') == false
|
||||
vfs.file_delete('/test_dir2/test.txt')!
|
||||
assert vfs.exists('/test_dir2/test.txt') == false
|
||||
assert vfs.exists('/test_dir2') == true
|
||||
|
||||
entries = vfs.dir_list('/test_dir')!
|
||||
entries = vfs.dir_list('/test_dir2')!
|
||||
assert entries.len == 0
|
||||
|
||||
// Test directory deletion
|
||||
vfs.dir_delete('/test_dir')!
|
||||
assert vfs.exists('/test_dir') == false
|
||||
vfs.dir_delete('/test_dir2')!
|
||||
assert vfs.exists('/test_dir2') == false
|
||||
|
||||
println('Test completed successfully!')
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user