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)!
|
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
|
// get_children returns all immediate children as FSEntry objects
|
||||||
pub fn (mut dir Directory) children(recursive bool) ![]FSEntry {
|
pub fn (mut dir Directory) children(recursive bool) ![]FSEntry {
|
||||||
mut entries := []FSEntry{}
|
mut entries := []FSEntry{}
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ import time
|
|||||||
@[heap]
|
@[heap]
|
||||||
pub struct OurDBFS {
|
pub struct OurDBFS {
|
||||||
pub mut:
|
pub mut:
|
||||||
root_id u32 // ID of root directory
|
root_id u32 // ID of root directory
|
||||||
block_size u32 // Size of data blocks in bytes
|
block_size u32 // Size of data blocks in bytes
|
||||||
data_dir string // Directory to store OurDBFS data
|
data_dir string // Directory to store OurDBFS data
|
||||||
metadata_dir string // Directory where we store the metadata
|
metadata_dir string // Directory where we store the metadata
|
||||||
db_data &ourdb.OurDB @[str: skip]// Database instance for persistent storage
|
db_data &ourdb.OurDB @[str: skip] // Database instance for persistent storage
|
||||||
db_meta &ourdb.OurDB @[str: skip]// Database instance for metadata storage
|
db_meta &ourdb.OurDB @[str: skip] // Database instance for metadata storage
|
||||||
last_inserted_id u32
|
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
|
// 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) {
|
if data := fs.db_meta.get(id) {
|
||||||
// First byte is version, second byte indicates the type
|
// First byte is version, second byte indicates the type
|
||||||
// TODO: check we dont overflow filetype (u8 in boundaries of filetype)
|
// TODO: check we dont overflow filetype (u8 in boundaries of filetype)
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ mut:
|
|||||||
get(path string) !FSEntry
|
get(path string) !FSEntry
|
||||||
rename(old_path string, new_path string) !
|
rename(old_path string, new_path string) !
|
||||||
copy(src_path string, dst_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) !
|
delete(path string) !
|
||||||
|
|
||||||
// Symlink operations
|
// 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}') }
|
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_src := myvfs.abs_path(src_path)
|
||||||
abs_dst := myvfs.abs_path(dst_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}') }
|
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
|
// Generic delete operation that handles all types
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ fn (self &NestedVFS) find_vfs(path string) !(vfscore.VFSImplementation, string)
|
|||||||
if path == '' || path == '/' {
|
if path == '' || path == '/' {
|
||||||
return self, '/'
|
return self, '/'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort prefixes by length (longest first) to match most specific path
|
// Sort prefixes by length (longest first) to match most specific path
|
||||||
mut prefixes := self.vfs_map.keys()
|
mut prefixes := self.vfs_map.keys()
|
||||||
prefixes.sort(a.len > b.len)
|
prefixes.sort(a.len > b.len)
|
||||||
@@ -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)
|
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 src_impl, src_rel_path := self.find_vfs(src_path)!
|
||||||
mut dst_impl, dst_rel_path := self.find_vfs(dst_path)!
|
_, dst_rel_path := self.find_vfs(dst_path)!
|
||||||
|
return src_impl.move(src_rel_path, dst_rel_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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut self NestedVFS) link_create(target_path string, link_path string) !vfscore.FSEntry {
|
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 {
|
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
|
// 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')
|
return error('Not implemented')
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut self OurDBVFS) move(src_path string, dst_path string) ! {
|
pub fn (mut self OurDBVFS) move(src_path string, dst_path string) !vfscore.FSEntry {
|
||||||
return error('Not implemented')
|
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 {
|
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')!
|
read_content := vfs.file_read('/test_dir/test.txt')!
|
||||||
assert read_content == test_content
|
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
|
// Test directory listing
|
||||||
mut entries := vfs.dir_list('/test_dir')!
|
mut entries := vfs.dir_list('/test_dir2')!
|
||||||
assert entries.len == 1
|
assert entries.len == 1
|
||||||
assert entries[0].get_metadata().name == 'test.txt'
|
assert entries[0].get_metadata().name == 'test.txt'
|
||||||
|
|
||||||
// Test exists
|
// Test exists
|
||||||
assert vfs.exists('/test_dir') == true
|
assert vfs.exists('/test_dir2') == true
|
||||||
assert vfs.exists('/test_dir/test.txt') == true
|
assert vfs.exists('/test_dir2/test.txt') == true
|
||||||
assert vfs.exists('/nonexistent') == false
|
assert vfs.exists('/nonexistent') == false
|
||||||
|
|
||||||
// Test symlink creation and reading
|
// Test symlink creation and reading
|
||||||
vfs.link_create('/test_dir/test.txt', '/test_dir/test_link')!
|
vfs.link_create('/test_dir2/test.txt', '/test_dir2/test_link')!
|
||||||
link_target := vfs.link_read('/test_dir/test_link')!
|
link_target := vfs.link_read('/test_dir2/test_link')!
|
||||||
assert link_target == '/test_dir/test.txt'
|
assert link_target == '/test_dir2/test.txt'
|
||||||
|
|
||||||
// Test symlink deletion
|
// Test symlink deletion
|
||||||
vfs.link_delete('/test_dir/test_link')!
|
vfs.link_delete('/test_dir2/test_link')!
|
||||||
assert vfs.exists('/test_dir/test_link') == false
|
assert vfs.exists('/test_dir2/test_link') == false
|
||||||
|
|
||||||
// Test file deletion
|
// Test file deletion
|
||||||
vfs.file_delete('/test_dir/test.txt')!
|
vfs.file_delete('/test_dir2/test.txt')!
|
||||||
assert vfs.exists('/test_dir/test.txt') == false
|
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
|
assert entries.len == 0
|
||||||
|
|
||||||
// Test directory deletion
|
// Test directory deletion
|
||||||
vfs.dir_delete('/test_dir')!
|
vfs.dir_delete('/test_dir2')!
|
||||||
assert vfs.exists('/test_dir') == false
|
assert vfs.exists('/test_dir2') == false
|
||||||
|
|
||||||
println('Test completed successfully!')
|
println('Test completed successfully!')
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user