feat: Improve directory copy functionality and add error handling
- Refactor `Directory.copy()` to use a struct for arguments, improving readability and maintainability. - Add comprehensive error handling to `Directory.copy()`, preventing unexpected failures and providing informative error messages. This includes handling cases where the source is not a directory, or a source and destination path are the same. - Implement recursive copying of directory contents, including files and symlinks. - Add unit tests to cover the new `copy` functionality and error handling. - Update `OurDBVFS.copy()` to utilize the improved `Directory.copy()` method and add input validation.
This commit is contained in:
@@ -93,22 +93,22 @@ pub fn (mut dir Directory) read(name string) !string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// str returns a formatted string of directory contents (non-recursive)
|
// str returns a formatted string of directory contents (non-recursive)
|
||||||
// pub fn (mut dir Directory) str() string {
|
pub fn (mut dir Directory) str() string {
|
||||||
// mut result := '${dir.metadata.name}/\n'
|
mut result := '${dir.metadata.name}/\n'
|
||||||
|
|
||||||
// for child_id in dir.children {
|
for child_id in dir.children {
|
||||||
// if entry := dir.myvfs.load_entry(child_id) {
|
if entry := dir.myvfs.load_entry(child_id) {
|
||||||
// if entry is Directory {
|
if entry is Directory {
|
||||||
// result += ' 📁 ${entry.metadata.name}/\n'
|
result += ' 📁 ${entry.metadata.name}/\n'
|
||||||
// } else if entry is File {
|
} else if entry is File {
|
||||||
// result += ' 📄 ${entry.metadata.name}\n'
|
result += ' 📄 ${entry.metadata.name}\n'
|
||||||
// } else if entry is Symlink {
|
} else if entry is Symlink {
|
||||||
// result += ' 🔗 ${entry.metadata.name} -> ${entry.target}\n'
|
result += ' 🔗 ${entry.metadata.name} -> ${entry.target}\n'
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// return result
|
return result
|
||||||
// }
|
}
|
||||||
|
|
||||||
// printall prints the directory structure recursively
|
// printall prints the directory structure recursively
|
||||||
pub fn (mut dir Directory) printall(indent string) !string {
|
pub fn (mut dir Directory) printall(indent string) !string {
|
||||||
@@ -313,48 +313,147 @@ fn move_children_recursive(mut dir Directory) ! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut dir Directory) copy(src_name string, dst_name string) !Directory {
|
pub struct CopyDirArgs {
|
||||||
|
pub mut:
|
||||||
|
src_entry_name string @[required] // source entry name
|
||||||
|
dst_entry_name string @[required] // destination entry name
|
||||||
|
dst_parent_dir &Directory @[required] // destination directory
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (mut dir Directory) copy(args_ CopyDirArgs) !&Directory {
|
||||||
mut found := false
|
mut found := false
|
||||||
mut new_entry := FSEntry(dir)
|
mut args := args_
|
||||||
current_time := time.now().unix()
|
|
||||||
|
|
||||||
for child_id in dir.children {
|
for child_id in dir.children {
|
||||||
if mut entry := dir.myvfs.load_entry(child_id) {
|
if mut entry := dir.myvfs.load_entry(child_id) {
|
||||||
if entry.metadata.name == src_name {
|
if entry.metadata.name == args.src_entry_name {
|
||||||
found = true
|
if entry is File {
|
||||||
new_entry = entry
|
return error('${args.src_entry_name} is a file, not a directory')
|
||||||
// Create a new copy
|
|
||||||
if entry is Directory {
|
|
||||||
mut entry_ := entry as Directory
|
|
||||||
mut new_dir := Directory{
|
|
||||||
metadata: entry_.metadata
|
|
||||||
children: entry_.children
|
|
||||||
parent_id: entry_.parent_id
|
|
||||||
myvfs: entry_.myvfs
|
|
||||||
}
|
|
||||||
|
|
||||||
new_dir.metadata.id = entry_.myvfs.get_next_id()
|
|
||||||
new_dir.metadata.name = dst_name
|
|
||||||
new_dir.metadata.created_at = current_time
|
|
||||||
new_dir.metadata.modified_at = current_time
|
|
||||||
new_dir.metadata.accessed_at = current_time
|
|
||||||
|
|
||||||
dir.children << new_dir.metadata.id
|
|
||||||
dir.metadata.modified_at = current_time
|
|
||||||
dir.metadata.id = dir.myvfs.save_entry(dir)!
|
|
||||||
|
|
||||||
dir.myvfs.save_entry(new_dir)!
|
|
||||||
return new_dir
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if entry is Symlink {
|
||||||
|
return error('${args.src_entry_name} is a symlink, not a directory')
|
||||||
|
}
|
||||||
|
|
||||||
|
found = true
|
||||||
|
mut src_dir := entry as Directory
|
||||||
|
|
||||||
|
// Create a new directory with copied metadata
|
||||||
|
current_time := time.now().unix()
|
||||||
|
mut new_dir := Directory{
|
||||||
|
metadata: Metadata{
|
||||||
|
id: args.dst_parent_dir.myvfs.get_next_id()
|
||||||
|
name: args.dst_entry_name
|
||||||
|
file_type: .directory
|
||||||
|
created_at: current_time
|
||||||
|
modified_at: current_time
|
||||||
|
accessed_at: current_time
|
||||||
|
mode: src_dir.metadata.mode
|
||||||
|
owner: src_dir.metadata.owner
|
||||||
|
group: src_dir.metadata.group
|
||||||
|
}
|
||||||
|
children: []u32{}
|
||||||
|
parent_id: args.dst_parent_dir.metadata.id
|
||||||
|
myvfs: args.dst_parent_dir.myvfs
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively copy children
|
||||||
|
copy_children_recursive(mut src_dir, mut new_dir)!
|
||||||
|
|
||||||
|
// Save new directory
|
||||||
|
args.dst_parent_dir.myvfs.save_entry(new_dir)!
|
||||||
|
args.dst_parent_dir.children << new_dir.metadata.id
|
||||||
|
args.dst_parent_dir.save()!
|
||||||
|
|
||||||
|
return &new_dir
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !found {
|
if !found {
|
||||||
return error('${src_name} not found')
|
return error('${args.src_entry_name} not found')
|
||||||
}
|
}
|
||||||
|
|
||||||
return &new_entry as Directory
|
return error('Unexpected copy failure')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn copy_children_recursive(mut src_dir Directory, mut dst_dir Directory) ! {
|
||||||
|
for child_id in src_dir.children {
|
||||||
|
if mut entry := src_dir.myvfs.load_entry(child_id) {
|
||||||
|
current_time := time.now().unix()
|
||||||
|
|
||||||
|
match entry {
|
||||||
|
Directory {
|
||||||
|
mut entry_ := entry as Directory
|
||||||
|
mut new_subdir := Directory{
|
||||||
|
metadata: Metadata{
|
||||||
|
id: dst_dir.myvfs.get_next_id()
|
||||||
|
name: entry_.metadata.name
|
||||||
|
file_type: .directory
|
||||||
|
created_at: current_time
|
||||||
|
modified_at: current_time
|
||||||
|
accessed_at: current_time
|
||||||
|
mode: entry_.metadata.mode
|
||||||
|
owner: entry_.metadata.owner
|
||||||
|
group: entry_.metadata.group
|
||||||
|
}
|
||||||
|
children: []u32{}
|
||||||
|
parent_id: dst_dir.metadata.id
|
||||||
|
myvfs: dst_dir.myvfs
|
||||||
|
}
|
||||||
|
|
||||||
|
copy_children_recursive(mut entry_, mut new_subdir)!
|
||||||
|
dst_dir.myvfs.save_entry(new_subdir)!
|
||||||
|
dst_dir.children << new_subdir.metadata.id
|
||||||
|
}
|
||||||
|
File {
|
||||||
|
mut entry_ := entry as File
|
||||||
|
mut new_file := File{
|
||||||
|
metadata: Metadata{
|
||||||
|
id: dst_dir.myvfs.get_next_id()
|
||||||
|
name: entry_.metadata.name
|
||||||
|
file_type: .file
|
||||||
|
size: entry_.metadata.size
|
||||||
|
created_at: current_time
|
||||||
|
modified_at: current_time
|
||||||
|
accessed_at: current_time
|
||||||
|
mode: entry_.metadata.mode
|
||||||
|
owner: entry_.metadata.owner
|
||||||
|
group: entry_.metadata.group
|
||||||
|
}
|
||||||
|
data: entry_.data
|
||||||
|
parent_id: dst_dir.metadata.id
|
||||||
|
myvfs: dst_dir.myvfs
|
||||||
|
}
|
||||||
|
dst_dir.myvfs.save_entry(new_file)!
|
||||||
|
dst_dir.children << new_file.metadata.id
|
||||||
|
}
|
||||||
|
Symlink {
|
||||||
|
mut entry_ := entry as Symlink
|
||||||
|
mut new_symlink := Symlink{
|
||||||
|
metadata: Metadata{
|
||||||
|
id: dst_dir.myvfs.get_next_id()
|
||||||
|
name: entry_.metadata.name
|
||||||
|
file_type: .symlink
|
||||||
|
created_at: current_time
|
||||||
|
modified_at: current_time
|
||||||
|
accessed_at: current_time
|
||||||
|
mode: entry_.metadata.mode
|
||||||
|
owner: entry_.metadata.owner
|
||||||
|
group: entry_.metadata.group
|
||||||
|
}
|
||||||
|
target: entry_.target
|
||||||
|
parent_id: dst_dir.metadata.id
|
||||||
|
myvfs: dst_dir.myvfs
|
||||||
|
}
|
||||||
|
dst_dir.myvfs.save_entry(new_symlink)!
|
||||||
|
dst_dir.children << new_symlink.metadata.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dst_dir.save()!
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (dir Directory) rename(src_name string, dst_name string) !&Directory {
|
pub fn (dir Directory) rename(src_name string, dst_name string) !&Directory {
|
||||||
|
|||||||
@@ -137,11 +137,32 @@ pub fn (mut self OurDBVFS) rename(old_path string, new_path string) !vfscore.FSE
|
|||||||
|
|
||||||
pub fn (mut self OurDBVFS) copy(src_path string, dst_path string) !vfscore.FSEntry {
|
pub fn (mut self OurDBVFS) copy(src_path string, dst_path string) !vfscore.FSEntry {
|
||||||
src_parent_path := os.dir(src_path)
|
src_parent_path := os.dir(src_path)
|
||||||
|
dst_parent_path := os.dir(dst_path)
|
||||||
|
|
||||||
|
if !self.exists(src_parent_path) {
|
||||||
|
return error('${src_parent_path} does not exist')
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.exists(dst_parent_path) {
|
||||||
|
return error('${dst_parent_path} does not exist')
|
||||||
|
}
|
||||||
|
|
||||||
src_name := os.base(src_path)
|
src_name := os.base(src_path)
|
||||||
dst_name := os.base(dst_path)
|
dst_name := os.base(dst_path)
|
||||||
|
|
||||||
mut src_parent_dir := self.get_directory(src_parent_path)!
|
mut src_parent_dir := self.get_directory(src_parent_path)!
|
||||||
copied_dir := src_parent_dir.copy(src_name, dst_name)!
|
mut dst_parent_dir := self.get_directory(dst_parent_path)!
|
||||||
|
|
||||||
|
if src_parent_dir == dst_parent_dir && src_name == dst_name {
|
||||||
|
return error('Moving to the same path not supported')
|
||||||
|
}
|
||||||
|
|
||||||
|
copied_dir := src_parent_dir.copy(
|
||||||
|
src_entry_name: src_name
|
||||||
|
dst_entry_name: dst_name
|
||||||
|
dst_parent_dir: dst_parent_dir
|
||||||
|
)!
|
||||||
|
|
||||||
return convert_to_vfscore_entry(copied_dir)
|
return convert_to_vfscore_entry(copied_dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -81,6 +81,23 @@ fn test_directory_move() ! {
|
|||||||
assert vfs.exists('/test_dir2/test.txt') == true
|
assert vfs.exists('/test_dir2/test.txt') == true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn test_directory_copy() ! {
|
||||||
|
mut vfs, data_dir, meta_dir := setup_vfs()!
|
||||||
|
defer {
|
||||||
|
teardown_vfs(data_dir, meta_dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
vfs.dir_create('/test_dir')!
|
||||||
|
vfs.file_create('/test_dir/test.txt')!
|
||||||
|
|
||||||
|
// Perform copy
|
||||||
|
copied_dir := vfs.copy('/test_dir', '/test_dir2')!
|
||||||
|
assert copied_dir.get_metadata().name == 'test_dir2'
|
||||||
|
assert vfs.exists('/test_dir') == true
|
||||||
|
assert vfs.exists('/test_dir/test.txt') == true
|
||||||
|
assert vfs.exists('/test_dir2/test.txt') == true
|
||||||
|
}
|
||||||
|
|
||||||
fn test_nested_directory_move() ! {
|
fn test_nested_directory_move() ! {
|
||||||
mut vfs, data_dir, meta_dir := setup_vfs()!
|
mut vfs, data_dir, meta_dir := setup_vfs()!
|
||||||
defer {
|
defer {
|
||||||
|
|||||||
Reference in New Issue
Block a user