fix vfs_db and add metadata_db

This commit is contained in:
timurgordon
2025-03-04 00:49:57 +01:00
parent 485b47d145
commit 913b0cb790
20 changed files with 2535 additions and 203 deletions

View File

@@ -1,53 +1,10 @@
module vfs_db
import freeflowuniverse.herolib.data.ourdb
import freeflowuniverse.herolib.core.pathlib
// Factory method for creating a new DatabaseVFS instance
@[params]
pub struct VFSParams {
pub:
data_dir string // Directory to store DatabaseVFS data
metadata_dir string // Directory to store metadata (defaults to data_dir if not specified)
incremental_mode bool // Whether to enable incremental mode
}
// Factory method for creating a new DatabaseVFS instance
pub fn new(mut database Database, params VFSParams) !&DatabaseVFS {
pathlib.get_dir(path: params.data_dir, create: true) or {
return error('Failed to create data directory: ${err}')
}
// Use the same database for both data and metadata if only one is provided
pub fn new(mut data_db Database, mut metadata_db Database) !&DatabaseVFS {
mut fs := &DatabaseVFS{
root_id: 1
block_size: 1024 * 4
data_dir: params.data_dir
metadata_dir: if params.metadata_dir.len > 0 { params.metadata_dir } else{ params.data_dir}
db_data: database
db_metadata: database
}
return fs
}
// Factory method for creating a new DatabaseVFS instance with separate databases for data and metadata
pub fn new_with_separate_dbs(mut data_db Database, mut metadata_db Database, params VFSParams) !&DatabaseVFS {
pathlib.get_dir(path: params.data_dir, create: true) or {
return error('Failed to create data directory: ${err}')
}
if params.metadata_dir.len > 0 {
pathlib.get_dir(path: params.metadata_dir, create: true) or {
return error('Failed to create metadata directory: ${err}')
}
}
mut fs := &DatabaseVFS{
root_id: 1
block_size: 1024 * 4
data_dir: params.data_dir
metadata_dir: if params.metadata_dir.len > 0 { params.metadata_dir } else { params.data_dir}
db_data: data_db
db_metadata: metadata_db
}

View File

@@ -0,0 +1,35 @@
module vfs_db
import os
import freeflowuniverse.herolib.data.ourdb
import rand
fn test_new() {
test_data_dir := os.join_path(os.temp_dir(), 'vfsourdb_factory_test_${rand.string(3)}')
os.mkdir_all(test_data_dir)!
defer {
os.rmdir_all(test_data_dir) or {}
}
// Create separate databases for data and metadata
mut db_data := ourdb.new(
path: os.join_path(test_data_dir, 'data')
incremental_mode: false
)!
mut db_metadata := ourdb.new(
path: os.join_path(test_data_dir, 'metadata')
incremental_mode: false
)!
// Test the factory function
mut vfs := new(mut db_data, mut db_metadata)!
// Verify the VFS was created correctly
assert vfs.root_id == 1
assert vfs.block_size == 1024 * 4
assert vfs.db_data == db_data
assert vfs.db_metadata == db_metadata
assert vfs.last_inserted_id == 0
assert vfs.id_table.len == 0
}

View File

@@ -0,0 +1,77 @@
module vfs_db
import os
import freeflowuniverse.herolib.data.ourdb
import rand
fn setup_vfs() !&DatabaseVFS {
test_data_dir := os.join_path(os.temp_dir(), 'vfsourdb_id_table_test_${rand.string(3)}')
os.mkdir_all(test_data_dir)!
// Create separate databases for data and metadata
mut db_data := ourdb.new(
path: os.join_path(test_data_dir, 'data')
incremental_mode: false
)!
mut db_metadata := ourdb.new(
path: os.join_path(test_data_dir, 'metadata')
incremental_mode: false
)!
// Create VFS with separate databases for data and metadata
mut vfs := new(mut db_data, mut db_metadata)!
return vfs
}
fn test_set_get_database_id() ! {
mut vfs := setup_vfs()!
// Test setting and getting database IDs
vfs_id := u32(1)
db_id := u32(42)
// Set the database ID
vfs.set_database_id(vfs_id, db_id)!
// Get the database ID and verify it matches
retrieved_id := vfs.get_database_id(vfs_id)!
assert retrieved_id == db_id
}
fn test_get_nonexistent_id() ! {
mut vfs := setup_vfs()!
// Try to get a database ID that doesn't exist
if _ := vfs.get_database_id(999) {
assert false, 'Expected error when getting non-existent ID'
} else {
assert err.msg() == 'VFS ID 999 not found.'
}
}
fn test_multiple_ids() ! {
mut vfs := setup_vfs()!
// Set multiple IDs
vfs.set_database_id(1, 101)!
vfs.set_database_id(2, 102)!
vfs.set_database_id(3, 103)!
// Verify all IDs can be retrieved correctly
assert vfs.get_database_id(1)! == 101
assert vfs.get_database_id(2)! == 102
assert vfs.get_database_id(3)! == 103
}
fn test_update_id() ! {
mut vfs := setup_vfs()!
// Set an ID
vfs.set_database_id(1, 100)!
assert vfs.get_database_id(1)! == 100
// Update the ID
vfs.set_database_id(1, 200)!
assert vfs.get_database_id(1)! == 200
}

View File

@@ -0,0 +1,135 @@
module vfs_db
import os
import freeflowuniverse.herolib.data.ourdb
import freeflowuniverse.herolib.vfs
import rand
fn setup_vfs() !&DatabaseVFS {
test_data_dir := os.join_path(os.temp_dir(), 'vfsourdb_metadata_test_${rand.string(3)}')
os.mkdir_all(test_data_dir)!
// Create separate databases for data and metadata
mut db_data := ourdb.new(
path: os.join_path(test_data_dir, 'data')
incremental_mode: false
)!
mut db_metadata := ourdb.new(
path: os.join_path(test_data_dir, 'metadata')
incremental_mode: false
)!
// Create VFS with separate databases for data and metadata
mut vfs := new(mut db_data, mut db_metadata)!
return vfs
}
fn test_new_metadata_file() ! {
mut vfs := setup_vfs()!
// Test creating file metadata
metadata := vfs.new_metadata(
name: 'test_file.txt'
file_type: .file
size: 1024
)
// Verify the metadata
assert metadata.name == 'test_file.txt'
assert metadata.file_type == .file
assert metadata.size == 1024
assert metadata.mode == 0o644 // Default mode
assert metadata.owner == 'user' // Default owner
assert metadata.group == 'user' // Default group
assert metadata.id == 1 // First ID
}
fn test_new_metadata_directory() ! {
mut vfs := setup_vfs()!
// Test creating directory metadata
metadata := vfs.new_metadata(
name: 'test_dir'
file_type: .directory
size: 0
)
// Verify the metadata
assert metadata.name == 'test_dir'
assert metadata.file_type == .directory
assert metadata.size == 0
assert metadata.mode == 0o644 // Default mode
assert metadata.owner == 'user' // Default owner
assert metadata.group == 'user' // Default group
assert metadata.id == 1 // First ID
}
fn test_new_metadata_symlink() ! {
mut vfs := setup_vfs()!
// Test creating symlink metadata
metadata := vfs.new_metadata(
name: 'test_link'
file_type: .symlink
size: 0
)
// Verify the metadata
assert metadata.name == 'test_link'
assert metadata.file_type == .symlink
assert metadata.size == 0
assert metadata.mode == 0o644 // Default mode
assert metadata.owner == 'user' // Default owner
assert metadata.group == 'user' // Default group
assert metadata.id == 1 // First ID
}
fn test_new_metadata_custom_permissions() ! {
mut vfs := setup_vfs()!
// Test creating metadata with custom permissions
metadata := vfs.new_metadata(
name: 'custom_file.txt'
file_type: .file
size: 2048
mode: 0o755
owner: 'admin'
group: 'staff'
)
// Verify the metadata
assert metadata.name == 'custom_file.txt'
assert metadata.file_type == .file
assert metadata.size == 2048
assert metadata.mode == 0o755
assert metadata.owner == 'admin'
assert metadata.group == 'staff'
assert metadata.id == 1 // First ID
}
fn test_new_metadata_sequential_ids() ! {
mut vfs := setup_vfs()!
// Create multiple metadata objects and verify IDs are sequential
metadata1 := vfs.new_metadata(
name: 'file1.txt'
file_type: .file
size: 100
)
assert metadata1.id == 1
metadata2 := vfs.new_metadata(
name: 'file2.txt'
file_type: .file
size: 200
)
assert metadata2.id == 2
metadata3 := vfs.new_metadata(
name: 'file3.txt'
file_type: .file
size: 300
)
assert metadata3.id == 3
}

View File

@@ -0,0 +1,136 @@
module vfs_db
import freeflowuniverse.herolib.vfs
fn test_directory_get_metadata() {
// Create a directory with metadata
metadata := vfs.Metadata{
id: 1
name: 'test_dir'
file_type: .directory
size: 0
mode: 0o755
owner: 'user'
group: 'user'
created: 0
modified: 0
}
dir := Directory{
metadata: metadata
children: []
parent_id: 0
}
// Test get_metadata
retrieved_metadata := dir.get_metadata()
assert retrieved_metadata.id == 1
assert retrieved_metadata.name == 'test_dir'
assert retrieved_metadata.file_type == .directory
assert retrieved_metadata.size == 0
assert retrieved_metadata.mode == 0o755
assert retrieved_metadata.owner == 'user'
assert retrieved_metadata.group == 'user'
}
fn test_directory_get_path() {
// Create a directory with metadata
metadata := vfs.Metadata{
id: 1
name: 'test_dir'
file_type: .directory
size: 0
mode: 0o755
owner: 'user'
group: 'user'
created: 0
modified: 0
}
dir := Directory{
metadata: metadata
children: []
parent_id: 0
}
// Test get_path
path := dir.get_path()
assert path == 'test_dir'
}
fn test_directory_is_dir() {
// Create a directory with metadata
metadata := vfs.Metadata{
id: 1
name: 'test_dir'
file_type: .directory
size: 0
mode: 0o755
owner: 'user'
group: 'user'
created: 0
modified: 0
}
dir := Directory{
metadata: metadata
children: []
parent_id: 0
}
// Test is_dir
assert dir.is_dir() == true
assert dir.is_file() == false
assert dir.is_symlink() == false
}
fn test_directory_with_children() {
// Create a directory with children
metadata := vfs.Metadata{
id: 1
name: 'parent_dir'
file_type: .directory
size: 0
mode: 0o755
owner: 'user'
group: 'user'
created: 0
modified: 0
}
dir := Directory{
metadata: metadata
children: [u32(2), 3, 4]
parent_id: 0
}
// Test children
assert dir.children.len == 3
assert dir.children[0] == 2
assert dir.children[1] == 3
assert dir.children[2] == 4
}
fn test_directory_with_parent() {
// Create a directory with a parent
metadata := vfs.Metadata{
id: 2
name: 'child_dir'
file_type: .directory
size: 0
mode: 0o755
owner: 'user'
group: 'user'
created: 0
modified: 0
}
dir := Directory{
metadata: metadata
children: []
parent_id: 1
}
// Test parent_id
assert dir.parent_id == 1
}

View File

@@ -64,6 +64,7 @@ pub:
pub fn (mut fs DatabaseVFS) new_file(file NewFile) !&File {
f := File{
data: file.data
parent_id: file.parent_id
metadata: fs.new_metadata(NewMetadata{
name: file.name
mode: file.mode
@@ -87,5 +88,6 @@ pub fn (mut fs DatabaseVFS) copy_file(file File) !&File {
mode: file.metadata.mode
owner: file.metadata.owner
group: file.metadata.group
parent_id: file.parent_id
)
}

View File

@@ -0,0 +1,220 @@
module vfs_db
import freeflowuniverse.herolib.vfs
import os
import freeflowuniverse.herolib.data.ourdb
import rand
fn setup_vfs() !&DatabaseVFS {
test_data_dir := os.join_path(os.temp_dir(), 'vfsourdb_model_file_test_${rand.string(3)}')
os.mkdir_all(test_data_dir)!
// Create separate databases for data and metadata
mut db_data := ourdb.new(
path: os.join_path(test_data_dir, 'data')
incremental_mode: false
)!
mut db_metadata := ourdb.new(
path: os.join_path(test_data_dir, 'metadata')
incremental_mode: false
)!
// Create VFS with separate databases for data and metadata
mut vfs := new(mut db_data, mut db_metadata)!
return vfs
}
fn test_file_get_metadata() {
// Create a file with metadata
metadata := vfs.Metadata{
id: 1
name: 'test_file.txt'
file_type: .file
size: 13
mode: 0o644
owner: 'user'
group: 'user'
created: 0
modified: 0
}
file := File{
metadata: metadata
data: 'Hello, World!'
parent_id: 0
}
// Test get_metadata
retrieved_metadata := file.get_metadata()
assert retrieved_metadata.id == 1
assert retrieved_metadata.name == 'test_file.txt'
assert retrieved_metadata.file_type == .file
assert retrieved_metadata.size == 13
assert retrieved_metadata.mode == 0o644
assert retrieved_metadata.owner == 'user'
assert retrieved_metadata.group == 'user'
}
fn test_file_get_path() {
// Create a file with metadata
metadata := vfs.Metadata{
id: 1
name: 'test_file.txt'
file_type: .file
size: 13
mode: 0o644
owner: 'user'
group: 'user'
created: 0
modified: 0
}
file := File{
metadata: metadata
data: 'Hello, World!'
parent_id: 0
}
// Test get_path
path := file.get_path()
assert path == 'test_file.txt'
}
fn test_file_is_file() {
// Create a file with metadata
metadata := vfs.Metadata{
id: 1
name: 'test_file.txt'
file_type: .file
size: 13
mode: 0o644
owner: 'user'
group: 'user'
created: 0
modified: 0
}
file := File{
metadata: metadata
data: 'Hello, World!'
parent_id: 0
}
// Test is_file
assert file.is_file() == true
assert file.is_dir() == false
assert file.is_symlink() == false
}
fn test_file_write_read() {
// Create a file with metadata
metadata := vfs.Metadata{
id: 1
name: 'test_file.txt'
file_type: .file
size: 13
mode: 0o644
owner: 'user'
group: 'user'
created: 0
modified: 0
}
mut file := File{
metadata: metadata
data: 'Hello, World!'
parent_id: 0
}
// Test read
content := file.read()
assert content == 'Hello, World!'
// Test write
file.write('New content')
assert file.data == 'New content'
assert file.metadata.size == 11 // 'New content'.len
// Test read after write
new_content := file.read()
assert new_content == 'New content'
}
fn test_file_rename() {
// Create a file with metadata
metadata := vfs.Metadata{
id: 1
name: 'test_file.txt'
file_type: .file
size: 13
mode: 0o644
owner: 'user'
group: 'user'
created: 0
modified: 0
}
mut file := File{
metadata: metadata
data: 'Hello, World!'
parent_id: 0
}
// Test rename
file.rename('renamed_file.txt')
assert file.metadata.name == 'renamed_file.txt'
}
fn test_new_file() ! {
mut vfs := setup_vfs()!
// Test creating a new file
mut file := vfs.new_file(
name: 'test_file.txt'
data: 'Hello, World!'
)!
// Verify the file
assert file.metadata.name == 'test_file.txt'
assert file.metadata.file_type == .file
assert file.metadata.size == 13
assert file.metadata.mode == 0o644
assert file.metadata.owner == 'user'
assert file.metadata.group == 'user'
assert file.data == 'Hello, World!'
}
fn test_copy_file() ! {
mut vfs := setup_vfs()!
// Create a file to copy
original_file := File{
metadata: vfs.Metadata{
id: 1
name: 'original.txt'
file_type: .file
size: 13
mode: 0o755
owner: 'admin'
group: 'staff'
created: 0
modified: 0
}
data: 'Hello, World!'
parent_id: 0
}
// Test copying the file
copied_file := vfs.copy_file(original_file)!
// Verify the copied file
assert copied_file.metadata.name == 'original.txt'
assert copied_file.metadata.file_type == .file
assert copied_file.metadata.size == 13
assert copied_file.metadata.mode == 0o755
assert copied_file.metadata.owner == 'admin'
assert copied_file.metadata.group == 'staff'
assert copied_file.data == 'Hello, World!'
assert copied_file.metadata.id != original_file.metadata.id // Should have a new ID
}

View File

@@ -0,0 +1,185 @@
module vfs_db
import freeflowuniverse.herolib.vfs
fn test_fsentry_directory() {
// Create a directory entry
dir := Directory{
metadata: vfs.Metadata{
id: 1
name: 'test_dir'
file_type: .directory
size: 0
mode: 0o755
owner: 'user'
group: 'user'
created: 0
modified: 0
}
children: []
parent_id: 0
}
// Convert to FSEntry
entry := FSEntry(dir)
// Test methods
assert entry.get_metadata().id == 1
assert entry.get_metadata().name == 'test_dir'
assert entry.get_metadata().file_type == .directory
assert entry.get_path() == 'test_dir'
assert entry.is_dir() == true
assert entry.is_file() == false
assert entry.is_symlink() == false
}
fn test_fsentry_file() {
// Create a file entry
file := File{
metadata: vfs.Metadata{
id: 2
name: 'test_file.txt'
file_type: .file
size: 13
mode: 0o644
owner: 'user'
group: 'user'
created: 0
modified: 0
}
data: 'Hello, World!'
parent_id: 0
}
// Convert to FSEntry
entry := FSEntry(file)
// Test methods
assert entry.get_metadata().id == 2
assert entry.get_metadata().name == 'test_file.txt'
assert entry.get_metadata().file_type == .file
assert entry.get_path() == 'test_file.txt'
assert entry.is_dir() == false
assert entry.is_file() == true
assert entry.is_symlink() == false
}
fn test_fsentry_symlink() {
// Create a symlink entry
symlink := Symlink{
metadata: vfs.Metadata{
id: 3
name: 'test_link'
file_type: .symlink
size: 0
mode: 0o777
owner: 'user'
group: 'user'
created: 0
modified: 0
}
target: '/path/to/target'
parent_id: 0
}
// Convert to FSEntry
entry := FSEntry(symlink)
// Test methods
assert entry.get_metadata().id == 3
assert entry.get_metadata().name == 'test_link'
assert entry.get_metadata().file_type == .symlink
assert entry.get_path() == 'test_link'
assert entry.is_dir() == false
assert entry.is_file() == false
assert entry.is_symlink() == true
}
fn test_fsentry_match() {
// Create entries of different types
dir := Directory{
metadata: vfs.Metadata{
id: 1
name: 'test_dir'
file_type: .directory
size: 0
mode: 0o755
owner: 'user'
group: 'user'
created: 0
modified: 0
}
children: []
parent_id: 0
}
file := File{
metadata: vfs.Metadata{
id: 2
name: 'test_file.txt'
file_type: .file
size: 13
mode: 0o644
owner: 'user'
group: 'user'
created: 0
modified: 0
}
data: 'Hello, World!'
parent_id: 0
}
symlink := Symlink{
metadata: vfs.Metadata{
id: 3
name: 'test_link'
file_type: .symlink
size: 0
mode: 0o777
owner: 'user'
group: 'user'
created: 0
modified: 0
}
target: '/path/to/target'
parent_id: 0
}
// Test match with directory
dir_entry := FSEntry(dir)
match dir_entry {
Directory {
assert it.metadata.id == 1
assert it.metadata.name == 'test_dir'
}
File, Symlink {
assert false, 'Expected Directory type'
}
}
// Test match with file
file_entry := FSEntry(file)
match file_entry {
File {
assert it.metadata.id == 2
assert it.metadata.name == 'test_file.txt'
assert it.data == 'Hello, World!'
}
Directory, Symlink {
assert false, 'Expected File type'
}
}
// Test match with symlink
symlink_entry := FSEntry(symlink)
match symlink_entry {
Symlink {
assert it.metadata.id == 3
assert it.metadata.name == 'test_link'
assert it.target == '/path/to/target'
}
Directory, File {
assert false, 'Expected Symlink type'
}
}
}

View File

@@ -0,0 +1,159 @@
module vfs_db
import freeflowuniverse.herolib.vfs
fn test_symlink_get_metadata() {
// Create a symlink with metadata
metadata := vfs.Metadata{
id: 1
name: 'test_link'
file_type: .symlink
size: 0
mode: 0o777
owner: 'user'
group: 'user'
created: 0
modified: 0
}
symlink := Symlink{
metadata: metadata
target: '/path/to/target'
parent_id: 0
}
// Test get_metadata
retrieved_metadata := symlink.get_metadata()
assert retrieved_metadata.id == 1
assert retrieved_metadata.name == 'test_link'
assert retrieved_metadata.file_type == .symlink
assert retrieved_metadata.size == 0
assert retrieved_metadata.mode == 0o777
assert retrieved_metadata.owner == 'user'
assert retrieved_metadata.group == 'user'
}
fn test_symlink_get_path() {
// Create a symlink with metadata
metadata := vfs.Metadata{
id: 1
name: 'test_link'
file_type: .symlink
size: 0
mode: 0o777
owner: 'user'
group: 'user'
created: 0
modified: 0
}
symlink := Symlink{
metadata: metadata
target: '/path/to/target'
parent_id: 0
}
// Test get_path
path := symlink.get_path()
assert path == 'test_link'
}
fn test_symlink_is_symlink() {
// Create a symlink with metadata
metadata := vfs.Metadata{
id: 1
name: 'test_link'
file_type: .symlink
size: 0
mode: 0o777
owner: 'user'
group: 'user'
created: 0
modified: 0
}
symlink := Symlink{
metadata: metadata
target: '/path/to/target'
parent_id: 0
}
// Test is_symlink
assert symlink.is_symlink() == true
assert symlink.is_dir() == false
assert symlink.is_file() == false
}
fn test_symlink_update_target() ! {
// Create a symlink with metadata
metadata := vfs.Metadata{
id: 1
name: 'test_link'
file_type: .symlink
size: 0
mode: 0o777
owner: 'user'
group: 'user'
created: 0
modified: 0
}
mut symlink := Symlink{
metadata: metadata
target: '/path/to/target'
parent_id: 0
}
// Test update_target
symlink.update_target('/new/target/path')!
assert symlink.target == '/new/target/path'
}
fn test_symlink_get_target() ! {
// Create a symlink with metadata
metadata := vfs.Metadata{
id: 1
name: 'test_link'
file_type: .symlink
size: 0
mode: 0o777
owner: 'user'
group: 'user'
created: 0
modified: 0
}
mut symlink := Symlink{
metadata: metadata
target: '/path/to/target'
parent_id: 0
}
// Test get_target
target := symlink.get_target()!
assert target == '/path/to/target'
}
fn test_symlink_with_parent() {
// Create a symlink with a parent
metadata := vfs.Metadata{
id: 2
name: 'test_link'
file_type: .symlink
size: 0
mode: 0o777
owner: 'user'
group: 'user'
created: 0
modified: 0
}
symlink := Symlink{
metadata: metadata
target: '/path/to/target'
parent_id: 1
}
// Test parent_id
assert symlink.parent_id == 1
}

View File

@@ -1,5 +1,9 @@
module vfs_db
pub fn (mut fs DatabaseVFS) print() ! {
println(fs.directory_printall(fs.root_get_as_dir()!, '')!)
}
// str returns a formatted string of directory contents (non-recursive)
pub fn (mut fs DatabaseVFS) directory_print(dir Directory) string {
mut result := '${dir.metadata.name}/\n'

190
lib/vfs/vfs_db/print_test.v Normal file
View File

@@ -0,0 +1,190 @@
module vfs_db
import os
import freeflowuniverse.herolib.data.ourdb
import freeflowuniverse.herolib.vfs
import rand
fn setup_vfs() !(&DatabaseVFS, string) {
test_data_dir := os.join_path(os.temp_dir(), 'vfsourdb_print_test_${rand.string(3)}')
os.mkdir_all(test_data_dir)!
// Create separate databases for data and metadata
mut db_data := ourdb.new(
path: os.join_path(test_data_dir, 'data')
incremental_mode: false
)!
mut db_metadata := ourdb.new(
path: os.join_path(test_data_dir, 'metadata')
incremental_mode: false
)!
// Create VFS with separate databases for data and metadata
mut vfs := new(mut db_data, mut db_metadata)!
return vfs, test_data_dir
}
fn teardown_vfs(data_dir string) {
os.rmdir_all(data_dir) or {}
}
fn test_directory_print_empty() ! {
mut vfs, data_dir := setup_vfs()!
defer {
teardown_vfs(data_dir)
}
// Create an empty directory
mut dir := vfs.new_directory(
name: 'test_dir'
)!
// Test printing the empty directory
output := vfs.directory_print(dir)
// Verify the output
assert output == 'test_dir/\n'
}
fn test_directory_print_with_contents() ! {
mut vfs, data_dir := setup_vfs()!
defer {
teardown_vfs(data_dir)
}
// Create a directory with various contents
mut dir := vfs.new_directory(
name: 'test_dir'
)!
// Add a subdirectory
mut subdir := vfs.directory_mkdir(mut dir, 'subdir')!
// Add a file
mut file := vfs.directory_touch(dir, 'test_file.txt')!
// Add a symlink
mut symlink := Symlink{
metadata: vfs.Metadata{
id: vfs.get_next_id()
name: 'test_link'
file_type: .symlink
size: 0
mode: 0o777
owner: 'user'
group: 'user'
created: 0
modified: 0
}
target: '/path/to/target'
parent_id: dir.metadata.id
}
vfs.directory_add_symlink(mut dir, mut symlink)!
// Test printing the directory
output := vfs.directory_print(dir)
// Verify the output contains all entries
assert output.contains('test_dir/')
assert output.contains('📁 subdir/')
assert output.contains('📄 test_file.txt')
assert output.contains('🔗 test_link -> /path/to/target')
}
fn test_directory_printall_simple() ! {
mut vfs, data_dir := setup_vfs()!
defer {
teardown_vfs(data_dir)
}
// Create a simple directory structure
mut dir := vfs.new_directory(
name: 'root_dir'
)!
// Add a file
mut file := vfs.directory_touch(dir, 'test_file.txt')!
// Test printing the directory recursively
output := vfs.directory_printall(dir, '')!
// Verify the output
assert output.contains('📁 root_dir/')
assert output.contains('📄 test_file.txt')
}
fn test_directory_printall_nested() ! {
mut vfs, data_dir := setup_vfs()!
defer {
teardown_vfs(data_dir)
}
// Create a nested directory structure
mut root := vfs.new_directory(
name: 'root'
)!
// Add a subdirectory
mut subdir1 := vfs.directory_mkdir(mut root, 'subdir1')!
// Add a file to the root
mut root_file := vfs.directory_touch(root, 'root_file.txt')!
// Add a file to the subdirectory
mut subdir_file := vfs.directory_touch(subdir1, 'subdir_file.txt')!
// Add a nested subdirectory
mut subdir2 := vfs.directory_mkdir(mut subdir1, 'subdir2')!
// Add a file to the nested subdirectory
mut nested_file := vfs.directory_touch(subdir2, 'nested_file.txt')!
// Add a symlink to the nested subdirectory
mut symlink := Symlink{
metadata: vfs.Metadata{
id: vfs.get_next_id()
name: 'test_link'
file_type: .symlink
size: 0
mode: 0o777
owner: 'user'
group: 'user'
created: 0
modified: 0
}
target: '/path/to/target'
parent_id: subdir2.metadata.id
}
vfs.directory_add_symlink(mut subdir2, mut symlink)!
// Test printing the directory recursively
output := vfs.directory_printall(root, '')!
// Verify the output contains all entries with proper indentation
assert output.contains('📁 root/')
assert output.contains(' 📄 root_file.txt')
assert output.contains(' 📁 subdir1/')
assert output.contains(' 📄 subdir_file.txt')
assert output.contains(' 📁 subdir2/')
assert output.contains(' 📄 nested_file.txt')
assert output.contains(' 🔗 test_link -> /path/to/target')
}
fn test_directory_printall_empty() ! {
mut vfs, data_dir := setup_vfs()!
defer {
teardown_vfs(data_dir)
}
// Create an empty directory
mut dir := vfs.new_directory(
name: 'empty_dir'
)!
// Test printing the empty directory recursively
output := vfs.directory_printall(dir, '')!
// Verify the output
assert output == '📁 empty_dir/\n'
}

View File

@@ -11,8 +11,6 @@ pub struct DatabaseVFS {
pub mut:
root_id u32 // ID of root directory
block_size u32 // Size of data blocks in bytes
data_dir string // Directory to store DatabaseVFS data
metadata_dir string // Directory where we store the metadata
db_data &Database @[str: skip] // Database instance for file data storage
db_metadata &Database @[str: skip] // Database instance for metadata storage
last_inserted_id u32

View File

@@ -1,79 +1,7 @@
module vfs_db
import freeflowuniverse.herolib.vfs
// // write creates a new file or writes to an existing file
// pub fn (mut fs DatabaseVFS) directory_write(dir_ Directory, name string, content string) !&File {
// mut dir := dir_
// mut file := &File{}
// mut is_new := true
// // Check if file exists
// for child_id in dir.children {
// mut entry := fs.load_entry(child_id)!
// if entry.metadata.name == name {
// if mut entry is File {
// mut d := entry
// file = &d
// is_new = false
// break
// } else {
// return error('${name} exists but is not a file')
// }
// }
// }
// if is_new {
// // Create new file
// current_time := time.now().unix()
// file = &File{
// metadata: vfs.Metadata{
// id: fs.get_next_id()
// name: name
// file_type: .file
// size: u64(content.len)
// created_at: current_time
// modified_at: current_time
// accessed_at: current_time
// mode: 0o644
// owner: 'user'
// group: 'user'
// }
// data: content
// parent_id: dir.metadata.id
// }
// // Save new file to DB
// fs.save_entry(file)!
// // Update children list
// dir.children << file.metadata.id
// fs.save_entry(dir)!
// } else {
// // Update existing file
// file.write(content)
// fs.save_entry(file)!
// }
// return file
// }
// // read reads content from a file
// pub fn (mut dir Directory) directory_read(name string) !string {
// // Find file
// for child_id in dir.children {
// if mut entry := dir.myvfs.load_entry(child_id) {
// if entry.metadata.name == name {
// if mut entry is File {
// return entry.read()
// } else {
// return error('${name} is not a file')
// }
// }
// }
// }
// return error('File ${name} not found')
// }
import freeflowuniverse.herolib.vfs { Metadata }
import time
// mkdir creates a new directory with default permissions
pub fn (mut fs DatabaseVFS) directory_mkdir(mut dir Directory, name string) !&Directory {
@@ -121,20 +49,42 @@ pub fn (mut fs DatabaseVFS) new_directory(dir NewDirectory) !&Directory {
return &d
}
// mkdir creates a new directory with default permissions
// copy_directory creates a new directory with the same metadata as the source
pub fn (mut fs DatabaseVFS) copy_directory(dir Directory) !&Directory {
return fs.new_directory(
name: dir.metadata.name
mode: dir.metadata.mode
owner: dir.metadata.owner
group: dir.metadata.group
)
// Ensure we get a new ID that's different from the original
mut new_id := fs.get_next_id()
// Make sure the new ID is different from the original
if new_id == dir.metadata.id {
new_id = fs.get_next_id() // Get another ID if they happen to be the same
}
new_dir := Directory{
metadata: Metadata{
...dir.metadata
id: new_id
created_at: time.now().unix()
modified_at: time.now().unix()
accessed_at: time.now().unix()
}
children: []u32{}
parent_id: dir.parent_id
}
fs.save_entry(new_dir)!
return &new_dir
}
// touch creates a new empty file with default permissions
pub fn (mut fs DatabaseVFS) directory_touch(dir_ Directory, name string) !&File {
mut dir := dir_
// First, make sure we're working with the latest version of the directory
if updated_dir := fs.load_entry(dir.metadata.id) {
if updated_dir is Directory {
dir = updated_dir
}
}
// Check if file already exists
for child_id in dir.children {
if entry := fs.load_entry(child_id) {
@@ -143,14 +93,30 @@ pub fn (mut fs DatabaseVFS) directory_touch(dir_ Directory, name string) !&File
}
}
}
new_file := fs.new_file(
// Create new file with correct parent_id
mut new_file := fs.new_file(
parent_id: dir.metadata.id
name: name
name: name
)!
// Ensure parent_id is set correctly
if new_file.parent_id != dir.metadata.id {
new_file.parent_id = dir.metadata.id
fs.save_entry(new_file)!
}
// Update children list
dir.children << new_file.metadata.id
fs.save_entry(dir)!
// Reload the directory to ensure we have the latest version
if updated_dir := fs.load_entry(dir.metadata.id) {
if updated_dir is Directory {
dir = updated_dir
}
}
return new_file
}
@@ -160,6 +126,13 @@ pub fn (mut fs DatabaseVFS) directory_rm(mut dir Directory, name string) ! {
mut found_id := u32(0)
mut found_idx := 0
// First, make sure we're working with the latest version of the directory
if updated_dir := fs.load_entry(dir.metadata.id) {
if updated_dir is Directory {
dir = updated_dir
}
}
for i, child_id in dir.children {
if entry := fs.load_entry(child_id) {
if entry.metadata.name == name {
@@ -191,9 +164,16 @@ pub fn (mut fs DatabaseVFS) directory_rm(mut dir Directory, name string) ! {
fs.db_metadata.delete(file.metadata.id) or { return error('Failed to delete entry: ${err}') }
// Update children list
// Update children list - make sure we don't remove the wrong child
dir.children.delete(found_idx)
fs.save_entry(dir)!
// Reload the directory to ensure we have the latest version
if updated_dir := fs.load_entry(dir.metadata.id) {
if updated_dir is Directory {
dir = updated_dir
}
}
}
pub struct MoveDirArgs {
@@ -207,6 +187,14 @@ pub fn (mut fs DatabaseVFS) directory_move(dir_ Directory, args_ MoveDirArgs) !&
mut dir := dir_
mut args := args_
mut found := false
mut child_id_to_remove := u32(0)
// First, make sure we're working with the latest version of the directory
if updated_dir := fs.load_entry(dir.metadata.id) {
if updated_dir is Directory {
dir = updated_dir
}
}
for child_id in dir.children {
if mut entry := fs.load_entry(child_id) {
@@ -220,15 +208,12 @@ pub fn (mut fs DatabaseVFS) directory_move(dir_ Directory, args_ MoveDirArgs) !&
}
found = true
child_id_to_remove = child_id
mut entry_ := entry as Directory
entry_.metadata.name = args.dst_entry_name
entry_.metadata.modified()
entry_.metadata.modified_at = time.now().unix()
entry_.parent_id = args.dst_parent_dir.metadata.id
// Remove from old parent's children
dir.children = dir.children.filter(it != child_id)
fs.save_entry(dir)!
// Recursively update all child paths in moved directory
fs.move_children_recursive(mut entry_)!
@@ -237,9 +222,20 @@ pub fn (mut fs DatabaseVFS) directory_move(dir_ Directory, args_ MoveDirArgs) !&
args.dst_parent_dir.children << entry_.metadata.id
}
// Remove from old parent's children before saving the entry
dir.children = dir.children.filter(it != child_id_to_remove)
fs.save_entry(dir)!
fs.save_entry(entry_)!
fs.save_entry(args.dst_parent_dir)!
// Reload the source directory to ensure we have the latest version
if updated_src_dir := fs.load_entry(dir.metadata.id) {
if updated_src_dir is Directory {
dir = updated_src_dir
}
}
return &entry_
}
}
@@ -280,6 +276,13 @@ pub fn (mut fs DatabaseVFS) directory_copy(mut dir Directory, args_ CopyDirArgs)
mut found := false
mut args := args_
// First, make sure we're working with the latest version of the directory
if updated_dir := fs.load_entry(dir.metadata.id) {
if updated_dir is Directory {
dir = updated_dir
}
}
for child_id in dir.children {
if mut entry := fs.load_entry(child_id) {
if entry.metadata.name == args.src_entry_name {
@@ -294,14 +297,21 @@ pub fn (mut fs DatabaseVFS) directory_copy(mut dir Directory, args_ CopyDirArgs)
found = true
mut src_dir := entry as Directory
// Make sure we have the latest version of the source directory
if updated_src_dir := fs.load_entry(src_dir.metadata.id) {
if updated_src_dir is Directory {
src_dir = updated_src_dir
}
}
// Create a new directory with copied metadata
mut new_dir := fs.copy_directory(Directory{
...src_dir
metadata: vfs.Metadata{
metadata: Metadata{
...src_dir.metadata
name: args.dst_entry_name
}
parent_id: args.dst_parent_dir.metadata.id
children: []u32{}
})!
// Recursively copy children
@@ -311,6 +321,24 @@ pub fn (mut fs DatabaseVFS) directory_copy(mut dir Directory, args_ CopyDirArgs)
fs.save_entry(new_dir)!
args.dst_parent_dir.children << new_dir.metadata.id
fs.save_entry(args.dst_parent_dir)!
// Make sure to save the source directory too
fs.save_entry(src_dir)!
// Reload the source directory to ensure we have the latest version
if updated_src_dir := fs.load_entry(src_dir.metadata.id) {
if updated_src_dir is Directory {
src_dir = updated_src_dir
}
}
// Reload the parent directory to ensure we have the latest version
if updated_parent_dir := fs.load_entry(dir.metadata.id) {
if updated_parent_dir is Directory {
dir = updated_parent_dir
}
}
return new_dir
}
}
@@ -330,8 +358,11 @@ fn (mut fs DatabaseVFS) copy_children_recursive(mut src_dir Directory, mut dst_d
Directory {
mut entry_ := entry as Directory
mut new_subdir := fs.copy_directory(Directory{
...entry_
children: []u32{}
metadata: Metadata{
...entry_.metadata
id: fs.get_next_id()
}
children: []u32{}
parent_id: dst_dir.metadata.id
})!
@@ -342,7 +373,11 @@ fn (mut fs DatabaseVFS) copy_children_recursive(mut src_dir Directory, mut dst_d
File {
mut entry_ := entry as File
mut new_file := fs.copy_file(File{
...entry_
metadata: Metadata{
...entry_.metadata
id: fs.get_next_id()
}
data: entry_.data
parent_id: dst_dir.metadata.id
})!
dst_dir.children << new_file.metadata.id
@@ -350,15 +385,11 @@ fn (mut fs DatabaseVFS) copy_children_recursive(mut src_dir Directory, mut dst_d
Symlink {
mut entry_ := entry as Symlink
mut new_symlink := Symlink{
metadata: fs.new_metadata(
name: entry_.metadata.name
file_type: .symlink
size: u64(0)
mode: entry_.metadata.mode
owner: entry_.metadata.owner
group: entry_.metadata.group
)
target: entry_.target
metadata: Metadata{
...entry_.metadata
id: fs.get_next_id()
}
target: entry_.target
parent_id: dst_dir.metadata.id
}
fs.save_entry(new_symlink)!
@@ -373,17 +404,23 @@ fn (mut fs DatabaseVFS) copy_children_recursive(mut src_dir Directory, mut dst_d
pub fn (mut fs DatabaseVFS) directory_rename(dir Directory, src_name string, dst_name string) !&Directory {
mut found := false
mut dir_ := dir
for child_id in dir.children {
if mut entry := fs.load_entry(child_id) {
if entry.metadata.name == src_name {
if entry is File {
return error('${src_name} is a file')
}
if entry is Symlink {
return error('${src_name} is a symlink')
}
found = true
entry.metadata.name = dst_name
entry.metadata.modified()
fs.save_entry(entry)!
get_dir := entry as Directory
return &get_dir
mut dir_entry := entry as Directory
dir_entry.metadata.name = dst_name
dir_entry.metadata.modified_at = time.now().unix()
fs.save_entry(dir_entry)!
return &dir_entry
}
}
}
@@ -392,39 +429,32 @@ pub fn (mut fs DatabaseVFS) directory_rename(dir Directory, src_name string, dst
return error('${src_name} not found')
}
return &dir_
return error('Unexpected rename failure')
}
// get_children returns all immediate children as FSEntry objects
pub fn (mut fs DatabaseVFS) directory_children(mut dir Directory, recursive bool) ![]FSEntry {
mut entries := []FSEntry{}
// Make sure we're working with the latest version of the directory
if updated_dir := fs.load_entry(dir.metadata.id) {
if updated_dir is Directory {
dir = updated_dir
}
}
for child_id in dir.children {
entry := fs.load_entry(child_id)!
entries << entry
if recursive {
if entry is Directory {
mut d := entry
if entry := fs.load_entry(child_id) {
entries << entry
if recursive && entry is Directory {
mut d := entry as Directory
entries << fs.directory_children(mut d, true)!
}
}
}
return entries
return entries.clone()
}
// pub fn (mut dir Directory) delete() ! {
// // Delete all children first
// for child_id in dir.children {
// dir.myvfs.delete_entry(child_id) or {}
// }
// // Clear children list
// dir.children.clear()
// // Save the updated directory
// dir.myvfs.save_entry(dir) or { return error('Failed to save directory: ${err}') }
// }
// add_symlink adds an existing symlink to this directory
pub fn (mut fs DatabaseVFS) directory_add_symlink(mut dir Directory, mut symlink Symlink) ! {
// Check if name already exists

View File

@@ -0,0 +1,520 @@
module vfs_db
import os
import freeflowuniverse.herolib.data.ourdb
import freeflowuniverse.herolib.vfs { Metadata }
import rand
fn setup_vfs() !(&DatabaseVFS, string) {
test_data_dir := os.join_path(os.temp_dir(), 'vfsourdb_directory_test_${rand.string(3)}')
os.mkdir_all(test_data_dir)!
// Create separate databases for data and metadata
mut db_data := ourdb.new(
path: os.join_path(test_data_dir, 'data')
incremental_mode: false
)!
mut db_metadata := ourdb.new(
path: os.join_path(test_data_dir, 'metadata')
incremental_mode: false
)!
// Create VFS with separate databases for data and metadata
mut fs := new(mut db_data, mut db_metadata)!
return fs, test_data_dir
}
fn teardown_vfs(data_dir string) {
os.rmdir_all(data_dir) or {}
}
fn test_new_directory() ! {
mut fs, data_dir := setup_vfs()!
defer {
teardown_vfs(data_dir)
}
// Test creating a new directory
mut dir := fs.new_directory(
name: 'test_dir'
)!
// Verify the directory
assert dir.metadata.name == 'test_dir'
assert dir.metadata.file_type == .directory
assert dir.metadata.size == 0
assert dir.metadata.mode == 0o755 // Default mode for directories
assert dir.metadata.owner == 'user'
assert dir.metadata.group == 'user'
assert dir.children.len == 0
}
fn test_new_directory_with_custom_permissions() ! {
mut fs, data_dir := setup_vfs()!
defer {
teardown_vfs(data_dir)
}
// Test creating a directory with custom permissions
mut dir := fs.new_directory(
name: 'custom_dir'
mode: 0o700
owner: 'admin'
group: 'staff'
)!
// Verify the directory
assert dir.metadata.name == 'custom_dir'
assert dir.metadata.file_type == .directory
assert dir.metadata.size == 0
assert dir.metadata.mode == 0o700
assert dir.metadata.owner == 'admin'
assert dir.metadata.group == 'staff'
}
fn test_copy_directory() ! {
mut fs, data_dir := setup_vfs()!
defer {
teardown_vfs(data_dir)
}
// Create a directory to copy
original_dir := Directory{
metadata: Metadata{
id: 1
name: 'original_dir'
file_type: .directory
size: 0
mode: 0o755
owner: 'admin'
group: 'staff'
created_at: 0
modified_at: 0
accessed_at: 0
}
children: []
parent_id: 0
}
// Test copying the directory
copied_dir := fs.copy_directory(original_dir)!
// Verify the copied directory
assert copied_dir.metadata.name == 'original_dir'
assert copied_dir.metadata.file_type == .directory
assert copied_dir.metadata.size == 0
assert copied_dir.metadata.mode == 0o755
assert copied_dir.metadata.owner == 'admin'
assert copied_dir.metadata.group == 'staff'
assert copied_dir.children.len == 0
assert copied_dir.metadata.id != original_dir.metadata.id // Should have a new ID
}
fn test_directory_mkdir() ! {
mut fs, data_dir := setup_vfs()!
defer {
teardown_vfs(data_dir)
}
// Create a parent directory
mut parent_dir := fs.new_directory(
name: 'parent_dir'
)!
// Test creating a subdirectory
mut subdir := fs.directory_mkdir(mut parent_dir, 'subdir')!
// Verify the subdirectory
assert subdir.metadata.name == 'subdir'
assert subdir.metadata.file_type == .directory
assert subdir.parent_id == parent_dir.metadata.id
// Verify the parent directory's children
assert parent_dir.children.len == 1
assert parent_dir.children[0] == subdir.metadata.id
// Test creating a duplicate directory (should fail)
if _ := fs.directory_mkdir(mut parent_dir, 'subdir') {
assert false, 'Expected error when creating duplicate directory'
} else {
assert err.msg().contains('already exists')
}
}
fn test_directory_touch() ! {
mut fs, data_dir := setup_vfs()!
defer {
teardown_vfs(data_dir)
}
// Create a parent directory
mut parent_dir := fs.new_directory(
name: 'parent_dir'
)!
// Test creating a file
mut file := fs.directory_touch(parent_dir, 'test_file.txt')!
// Reload the parent directory to get the latest version
if updated_dir := fs.load_entry(parent_dir.metadata.id) {
if updated_dir is Directory {
mut dir := updated_dir as Directory
parent_dir.children = dir.children
}
}
// Verify the file
assert file.metadata.name == 'test_file.txt'
assert file.metadata.file_type == .file
assert file.parent_id == parent_dir.metadata.id
assert file.data == '' // Should be empty
// Verify the parent directory's children
assert parent_dir.children.len == 1
assert parent_dir.children[0] == file.metadata.id
// Test creating a duplicate file (should fail)
if _ := fs.directory_touch(parent_dir, 'test_file.txt') {
assert false, 'Expected error when creating duplicate file'
} else {
assert err.msg().contains('already exists')
}
}
fn test_directory_rm() ! {
mut fs, data_dir := setup_vfs()!
defer {
teardown_vfs(data_dir)
}
// Create a parent directory
mut parent_dir := fs.new_directory(
name: 'parent_dir'
)!
// Create a file to remove
mut file := fs.directory_touch(parent_dir, 'test_file.txt')!
// Reload the parent directory to get the latest version
if updated_dir := fs.load_entry(parent_dir.metadata.id) {
if updated_dir is Directory {
mut dir := updated_dir as Directory
parent_dir.children = dir.children
}
}
// Verify the parent directory's children
assert parent_dir.children.len == 1
// Test removing the file
fs.directory_rm(mut parent_dir, 'test_file.txt')!
// Reload the parent directory to get the latest version
if updated_dir := fs.load_entry(parent_dir.metadata.id) {
if updated_dir is Directory {
mut dir := updated_dir as Directory
parent_dir.children = dir.children
}
}
// Verify the parent directory's children
assert parent_dir.children.len == 0
// Test removing a non-existent file (should fail)
if _ := fs.directory_rm(mut parent_dir, 'nonexistent.txt') {
assert false, 'Expected error when removing non-existent file'
} else {
assert err.msg().contains('not found')
}
}
fn test_directory_rename() ! {
mut fs, data_dir := setup_vfs()!
defer {
teardown_vfs(data_dir)
}
// Create a parent directory
mut parent_dir := fs.new_directory(
name: 'parent_dir'
)!
// Create a subdirectory to rename
mut subdir := fs.directory_mkdir(mut parent_dir, 'old_name')!
// Test renaming the subdirectory
renamed_dir := fs.directory_rename(parent_dir, 'old_name', 'new_name')!
// Verify the renamed directory
assert renamed_dir.metadata.name == 'new_name'
// Test renaming a non-existent directory (should fail)
if _ := fs.directory_rename(parent_dir, 'nonexistent', 'new_name') {
assert false, 'Expected error when renaming non-existent directory'
} else {
assert err.msg().contains('not found')
}
}
fn test_directory_children() ! {
mut fs, data_dir := setup_vfs()!
defer {
teardown_vfs(data_dir)
}
// Create a parent directory
mut parent_dir := fs.new_directory(
name: 'parent_dir'
)!
ch := fs.directory_children(mut parent_dir, false)!
panic(ch)
// Create subdirectories and files
mut subdir1 := fs.directory_mkdir(mut parent_dir, 'subdir1')!
mut subdir2 := fs.directory_mkdir(mut parent_dir, 'subdir2')!
mut file1 := fs.directory_touch(parent_dir, 'file1.txt')!
// Create a nested file
mut nested_file := fs.directory_touch(subdir1, 'nested.txt')!
// Test getting non-recursive children
children := fs.directory_children(mut parent_dir, false)!
assert children.len == 3
// Verify children types
mut dir_count := 0
mut file_count := 0
for child in children {
if child is Directory {
dir_count++
} else if child is File {
file_count++
}
}
assert dir_count == 2
assert file_count == 1
// Test getting recursive children
recursive_children := fs.directory_children(mut parent_dir, true)!
assert recursive_children.len == 4 // parent_dir's 3 children + nested_file
}
fn test_directory_move() ! {
mut fs, data_dir := setup_vfs()!
defer {
teardown_vfs(data_dir)
}
// Create source and destination parent directories
mut src_parent := fs.new_directory(name: 'src_parent')!
mut dst_parent := fs.new_directory(name: 'dst_parent')!
// Create a directory to move with nested structure
mut dir_to_move := fs.directory_mkdir(mut src_parent, 'dir_to_move')!
mut nested_dir := fs.directory_mkdir(mut dir_to_move, 'nested_dir')!
mut nested_file := fs.directory_touch(dir_to_move, 'nested_file.txt')!
// Reload the directories to get the latest versions
if updated_dir := fs.load_entry(src_parent.metadata.id) {
if updated_dir is Directory {
mut dir := updated_dir as Directory
src_parent.children = dir.children
}
}
if updated_dir := fs.load_entry(dst_parent.metadata.id) {
if updated_dir is Directory {
mut dir := updated_dir as Directory
dst_parent.children = dir.children
}
}
if updated_dir := fs.load_entry(dir_to_move.metadata.id) {
if updated_dir is Directory {
mut dir := updated_dir as Directory
dir_to_move.children = dir.children
}
}
// Test moving the directory
moved_dir := fs.directory_move(src_parent, MoveDirArgs{
src_entry_name: 'dir_to_move'
dst_entry_name: 'moved_dir'
dst_parent_dir: dst_parent
})!
// Reload the directories to get the latest versions
if updated_dir := fs.load_entry(src_parent.metadata.id) {
if updated_dir is Directory {
mut dir := updated_dir as Directory
src_parent.children = dir.children
}
}
if updated_dir := fs.load_entry(dst_parent.metadata.id) {
if updated_dir is Directory {
mut dir := updated_dir as Directory
dst_parent.children = dir.children
}
}
// Verify the moved directory
assert moved_dir.metadata.name == 'moved_dir'
assert moved_dir.parent_id == dst_parent.metadata.id
assert moved_dir.children.len == 2
// Verify source parent no longer has the directory
assert src_parent.children.len == 0
// Verify destination parent has the moved directory
assert dst_parent.children.len == 1
assert dst_parent.children[0] == moved_dir.metadata.id
// Test moving non-existent directory (should fail)
if _ := fs.directory_move(src_parent, MoveDirArgs{
src_entry_name: 'nonexistent'
dst_entry_name: 'new_name'
dst_parent_dir: dst_parent
}) {
assert false, 'Expected error when moving non-existent directory'
} else {
assert err.msg().contains('not found')
}
}
fn test_directory_copy() ! {
mut fs, data_dir := setup_vfs()!
defer {
teardown_vfs(data_dir)
}
// Create source and destination parent directories
mut src_parent := fs.new_directory(name: 'src_parent')!
mut dst_parent := fs.new_directory(name: 'dst_parent')!
// Create a directory to copy with nested structure
mut dir_to_copy := fs.directory_mkdir(mut src_parent, 'dir_to_copy')!
mut nested_dir := fs.directory_mkdir(mut dir_to_copy, 'nested_dir')!
mut nested_file := fs.directory_touch(dir_to_copy, 'nested_file.txt')!
// Reload the directories to get the latest versions
if updated_dir := fs.load_entry(src_parent.metadata.id) {
if updated_dir is Directory {
mut dir := updated_dir as Directory
src_parent.children = dir.children
}
}
if updated_dir := fs.load_entry(dst_parent.metadata.id) {
if updated_dir is Directory {
mut dir := updated_dir as Directory
dst_parent.children = dir.children
}
}
if updated_dir := fs.load_entry(dir_to_copy.metadata.id) {
if updated_dir is Directory {
mut dir := updated_dir as Directory
dir_to_copy.children = dir.children
}
}
// Test copying the directory
copied_dir := fs.directory_copy(mut src_parent, CopyDirArgs{
src_entry_name: 'dir_to_copy'
dst_entry_name: 'copied_dir'
dst_parent_dir: dst_parent
})!
// Reload the directories to get the latest versions
if updated_dir := fs.load_entry(src_parent.metadata.id) {
if updated_dir is Directory {
mut dir := updated_dir as Directory
src_parent.children = dir.children
}
}
if updated_dir := fs.load_entry(dst_parent.metadata.id) {
if updated_dir is Directory {
mut dir := updated_dir as Directory
dst_parent.children = dir.children
}
}
if updated_dir := fs.load_entry(dir_to_copy.metadata.id) {
if updated_dir is Directory {
mut dir := updated_dir as Directory
dir_to_copy.children = dir.children
}
}
// Verify the copied directory
assert copied_dir.metadata.name == 'copied_dir'
assert copied_dir.parent_id == dst_parent.metadata.id
assert copied_dir.children.len == 2
// Verify source directory still exists with its children
assert src_parent.children.len == 1
assert dir_to_copy.children.len == 2
// Verify destination parent has the copied directory
assert dst_parent.children.len == 1
assert dst_parent.children[0] == copied_dir.metadata.id
// Test copying non-existent directory (should fail)
if _ := fs.directory_copy(mut src_parent, CopyDirArgs{
src_entry_name: 'nonexistent'
dst_entry_name: 'new_copy'
dst_parent_dir: dst_parent
}) {
assert false, 'Expected error when copying non-existent directory'
} else {
assert err.msg().contains('not found')
}
}
fn test_directory_add_symlink() ! {
mut fs, data_dir := setup_vfs()!
defer {
teardown_vfs(data_dir)
}
// Create a parent directory
mut parent_dir := fs.new_directory(
name: 'parent_dir'
)!
// Create a symlink
mut symlink := Symlink{
metadata: Metadata{
id: fs.get_next_id()
name: 'test_link'
file_type: .symlink
size: 0
mode: 0o777
owner: 'user'
group: 'user'
created_at: 0
modified_at: 0
accessed_at: 0
}
target: '/path/to/target'
parent_id: parent_dir.metadata.id
}
// Test adding the symlink to the directory
fs.directory_add_symlink(mut parent_dir, mut symlink)!
// Verify the parent directory's children
assert parent_dir.children.len == 1
assert parent_dir.children[0] == symlink.metadata.id
// Test adding a duplicate symlink (should fail)
if _ := fs.directory_add_symlink(mut parent_dir, mut symlink) {
assert false, 'Expected error when adding duplicate symlink'
} else {
assert err.msg().contains('already exists')
}
}

View File

@@ -0,0 +1,223 @@
module vfs_db
import os
import freeflowuniverse.herolib.data.ourdb
import freeflowuniverse.herolib.vfs
import rand
fn setup_vfs() !(&DatabaseVFS, string) {
test_data_dir := os.join_path(os.temp_dir(), 'vfsourdb_getters_test_${rand.string(3)}')
os.mkdir_all(test_data_dir)!
// Create separate databases for data and metadata
mut db_data := ourdb.new(
path: os.join_path(test_data_dir, 'data')
incremental_mode: false
)!
mut db_metadata := ourdb.new(
path: os.join_path(test_data_dir, 'metadata')
incremental_mode: false
)!
// Create VFS with separate databases for data and metadata
mut vfs := new(mut db_data, mut db_metadata)!
return vfs, test_data_dir
}
fn teardown_vfs(data_dir string) {
os.rmdir_all(data_dir) or {}
}
fn test_root_get_as_dir() ! {
mut vfs, data_dir := setup_vfs()!
defer {
teardown_vfs(data_dir)
}
// Test getting the root directory
mut root := vfs.root_get_as_dir()!
// Verify the root directory
assert root.metadata.name == ''
assert root.metadata.file_type == .directory
assert root.metadata.mode == 0o755
assert root.metadata.owner == 'user'
assert root.metadata.group == 'user'
assert root.parent_id == 0
// Test getting the root directory again (should be the same)
mut root2 := vfs.root_get_as_dir()!
assert root2.metadata.id == root.metadata.id
}
fn test_get_entry_root() ! {
mut vfs, data_dir := setup_vfs()!
defer {
teardown_vfs(data_dir)
}
// Test getting the root entry with different path formats
root1 := vfs.get_entry('/')!
root2 := vfs.get_entry('')!
root3 := vfs.get_entry('.')!
// Verify all paths return the root directory
assert root1 is Directory
assert root2 is Directory
assert root3 is Directory
if root1 is Directory {
assert root1.metadata.name == ''
}
}
fn test_get_entry_file() ! {
mut vfs, data_dir := setup_vfs()!
defer {
teardown_vfs(data_dir)
}
// Create a file in the root directory
mut root := vfs.root_get_as_dir()!
mut file := vfs.directory_touch(root, 'test_file.txt')!
// Test getting the file entry
entry := vfs.get_entry('/test_file.txt')!
// Verify the entry is a file
assert entry is File
if entry is File {
assert entry.metadata.name == 'test_file.txt'
assert entry.metadata.file_type == .file
}
}
fn test_get_entry_directory() ! {
mut vfs, data_dir := setup_vfs()!
defer {
teardown_vfs(data_dir)
}
// Create a directory in the root directory
mut root := vfs.root_get_as_dir()!
mut dir := vfs.directory_mkdir(mut root, 'test_dir')!
// Test getting the directory entry
entry := vfs.get_entry('/test_dir')!
// Verify the entry is a directory
assert entry is Directory
if entry is Directory {
assert entry.metadata.name == 'test_dir'
assert entry.metadata.file_type == .directory
}
}
fn test_get_entry_nested() ! {
mut vfs, data_dir := setup_vfs()!
defer {
teardown_vfs(data_dir)
}
// Create a nested directory structure
mut root := vfs.root_get_as_dir()!
mut dir1 := vfs.directory_mkdir(mut root, 'dir1')!
mut dir2 := vfs.directory_mkdir(mut dir1, 'dir2')!
mut file := vfs.directory_touch(dir2, 'nested_file.txt')!
// Test getting the nested file entry
entry := vfs.get_entry('/dir1/dir2/nested_file.txt')!
// Verify the entry is a file
assert entry is File
if entry is File {
assert entry.metadata.name == 'nested_file.txt'
assert entry.metadata.file_type == .file
}
}
fn test_get_entry_not_found() ! {
mut vfs, data_dir := setup_vfs()!
defer {
teardown_vfs(data_dir)
}
// Test getting a non-existent entry
if _ := vfs.get_entry('/nonexistent') {
assert false, 'Expected error when getting non-existent entry'
} else {
assert err.msg().contains('Path not found')
}
}
fn test_get_entry_not_a_directory() ! {
mut vfs, data_dir := setup_vfs()!
defer {
teardown_vfs(data_dir)
}
// Create a file in the root directory
mut root := vfs.root_get_as_dir()!
mut file := vfs.directory_touch(root, 'test_file.txt')!
// Test getting an entry through a file (should fail)
if _ := vfs.get_entry('/test_file.txt/something') {
assert false, 'Expected error when traversing through a file'
} else {
assert err.msg().contains('Not a directory')
}
}
fn test_get_directory() ! {
mut vfs, data_dir := setup_vfs()!
defer {
teardown_vfs(data_dir)
}
// Create a directory in the root directory
mut root := vfs.root_get_as_dir()!
mut dir := vfs.directory_mkdir(mut root, 'test_dir')!
// Test getting the directory
retrieved_dir := vfs.get_directory('/test_dir')!
// Verify the directory
assert retrieved_dir.metadata.name == 'test_dir'
assert retrieved_dir.metadata.file_type == .directory
}
fn test_get_directory_root() ! {
mut vfs, data_dir := setup_vfs()!
defer {
teardown_vfs(data_dir)
}
// Test getting the root directory
root := vfs.get_directory('/')!
// Verify the root directory
assert root.metadata.name == ''
assert root.metadata.file_type == .directory
}
fn test_get_directory_not_a_directory() ! {
mut vfs, data_dir := setup_vfs()!
defer {
teardown_vfs(data_dir)
}
// Create a file in the root directory
mut root := vfs.root_get_as_dir()!
mut file := vfs.directory_touch(root, 'test_file.txt')!
// Test getting a file as a directory (should fail)
if _ := vfs.get_directory('/test_file.txt') {
assert false, 'Expected error when getting a file as a directory'
} else {
assert err.msg().contains('Not a directory')
}
}

View File

@@ -1,6 +1,8 @@
module vfs_db
import freeflowuniverse.herolib.vfs
import freeflowuniverse.herolib.core.texttools
import log
import os
import time
@@ -10,6 +12,7 @@ pub fn (mut fs DatabaseVFS) root_get() !vfs.FSEntry {
}
pub fn (mut self DatabaseVFS) file_create(path string) !vfs.FSEntry {
log.info('[DatabaseVFS] Creating file ${path}')
// Get parent directory
parent_path := os.dir(path)
file_name := os.base(path)
@@ -19,6 +22,7 @@ pub fn (mut self DatabaseVFS) file_create(path string) !vfs.FSEntry {
}
pub fn (mut self DatabaseVFS) file_read(path string) ![]u8 {
log.info('[DatabaseVFS] Reading file ${path}')
mut entry := self.get_entry(path)!
if mut entry is File {
return entry.read().bytes()
@@ -27,12 +31,18 @@ pub fn (mut self DatabaseVFS) file_read(path string) ![]u8 {
}
pub fn (mut self DatabaseVFS) file_write(path string, data []u8) ! {
mut entry := self.get_entry(path)!
if mut entry is File {
entry.write(data.bytestr())
self.save_entry(entry)!
if mut entry := self.get_entry(path) {
if mut entry is File {
log.info('[DatabaseVFS] Writing file ${path}')
log.info('[DatabaseVFS] Writing data ${data.bytestr()}')
entry.write(data.bytestr())
self.save_entry(entry)!
} else {
panic('handle error')
}
} else {
return error('Not a file: ${path}')
self.file_create(path)!
self.file_write(path, data)!
}
}
@@ -45,6 +55,7 @@ pub fn (mut self DatabaseVFS) file_delete(path string) ! {
}
pub fn (mut self DatabaseVFS) dir_create(path string) !vfs.FSEntry {
log.info('[DatabaseVFS] Creating Directory ${path}')
parent_path := os.dir(path)
dir_name := os.base(path)
@@ -53,8 +64,19 @@ pub fn (mut self DatabaseVFS) dir_create(path string) !vfs.FSEntry {
}
pub fn (mut self DatabaseVFS) dir_list(path string) ![]vfs.FSEntry {
log.info('[DatabaseVFS] Listing Directory ${path}')
mut dir := self.get_directory(path)!
return self.directory_children(mut dir, false)!.map(vfs.FSEntry(it))
mut entries := []vfs.FSEntry{}
for child in self.directory_children(mut dir, false)! {
if child is File {
entries << vfs.FSEntry(child)
} else if child is Directory {
entries << vfs.FSEntry(child)
} else if child is Symlink {
entries << vfs.FSEntry(child)
}
}
return entries
}
pub fn (mut self DatabaseVFS) dir_delete(path string) ! {
@@ -125,6 +147,7 @@ pub fn (mut fs DatabaseVFS) get(path string) !vfs.FSEntry {
}
pub fn (mut self DatabaseVFS) rename(old_path string, new_path string) !vfs.FSEntry {
log.info('[DatabaseVFS] Renaming ${old_path} to ${new_path}')
src_parent_path := os.dir(old_path)
src_name := os.base(old_path)
dst_name := os.base(new_path)
@@ -163,8 +186,11 @@ pub fn (mut self DatabaseVFS) copy(src_path string, dst_path string) !vfs.FSEntr
}
pub fn (mut self DatabaseVFS) move(src_path string, dst_path string) !vfs.FSEntry {
log.info('[DatabaseVFS] Moving ${src_path} to ${dst_path}')
src_parent_path := os.dir(src_path)
dst_parent_path := os.dir(dst_path)
log.info('${src_parent_path}')
if !self.exists(src_parent_path) {
return error('${src_parent_path} does not exist')

View File

@@ -10,11 +10,16 @@ fn setup_vfs() !(&DatabaseVFS, string) {
os.mkdir_all(test_data_dir)!
mut db_data := ourdb.new(
path: test_data_dir
path: os.join_path(test_data_dir, 'data')
incremental_mode: false
)!
mut vfs := new(mut db_data, data_dir: test_data_dir)!
mut db_metadata := ourdb.new(
path: os.join_path(test_data_dir, 'metadata')
incremental_mode: false
)!
mut vfs := new(mut db_data, mut db_metadata)!
return vfs, test_data_dir
}
@@ -152,8 +157,95 @@ fn test_deletion_operations() ! {
assert vfs.exists('/test_dir') == false
}
// Add more test functions for other operations like:
// - test_directory_copy()
// - test_symlink_operations()
// - test_directory_rename()
// - test_file_metadata()
fn test_symlink_operations() ! {
mut vfs, data_dir := setup_vfs()!
defer {
teardown_vfs(data_dir)
}
vfs.dir_create('/test_dir')!
vfs.file_create('/test_dir/target.txt')!
// Test symlink creation
mut symlink := vfs.link_create('/test_dir/target.txt', '/test_link')!
assert symlink.get_metadata().name == 'test_link'
assert symlink.get_metadata().file_type == .symlink
assert vfs.exists('/test_link') == true
// Test symlink reading
target := vfs.link_read('/test_link')!
assert target == '/test_dir/target.txt'
// Test symlink deletion
vfs.link_delete('/test_link')!
assert vfs.exists('/test_link') == false
}
fn test_rename_operations() ! {
mut vfs, data_dir := setup_vfs()!
defer {
teardown_vfs(data_dir)
}
// Test file rename
vfs.file_create('/test_file.txt')!
renamed_file := vfs.rename('/test_file.txt', '/renamed_file.txt')!
assert renamed_file.get_metadata().name == 'renamed_file.txt'
assert vfs.exists('/test_file.txt') == false
assert vfs.exists('/renamed_file.txt') == true
// Test directory rename
vfs.dir_create('/test_dir')!
renamed_dir := vfs.rename('/test_dir', '/renamed_dir')!
assert renamed_dir.get_metadata().name == 'renamed_dir'
assert vfs.exists('/test_dir') == false
assert vfs.exists('/renamed_dir') == true
}
fn test_exists_function() ! {
mut vfs, data_dir := setup_vfs()!
defer {
teardown_vfs(data_dir)
}
// Test root exists
assert vfs.exists('/') == true
// Test non-existent path
assert vfs.exists('/nonexistent') == false
// Create and test file exists
vfs.file_create('/test_file.txt')!
assert vfs.exists('/test_file.txt') == true
// Create and test directory exists
vfs.dir_create('/test_dir')!
assert vfs.exists('/test_dir') == true
// Test with and without leading slash
assert vfs.exists('test_dir') == true
}
fn test_get_function() ! {
mut vfs, data_dir := setup_vfs()!
defer {
teardown_vfs(data_dir)
}
// Test getting root
root := vfs.get('/')!
assert root.get_metadata().name == ''
assert root.get_metadata().file_type == .directory
// Test getting file
vfs.file_create('/test_file.txt')!
file := vfs.get('/test_file.txt')!
assert file.get_metadata().name == 'test_file.txt'
assert file.get_metadata().file_type == .file
// Test getting directory
vfs.dir_create('/test_dir')!
dir := vfs.get('/test_dir')!
assert dir.get_metadata().name == 'test_dir'
assert dir.get_metadata().file_type == .directory
}

196
lib/vfs/vfs_db/vfs_test.v Normal file
View File

@@ -0,0 +1,196 @@
module vfs_db
import os
import freeflowuniverse.herolib.data.ourdb
import rand
fn setup_vfs() !(&DatabaseVFS, string) {
test_data_dir := os.join_path(os.temp_dir(), 'vfsourdb_vfs_test_${rand.string(3)}')
os.mkdir_all(test_data_dir)!
// Create separate databases for data and metadata
mut db_data := ourdb.new(
path: os.join_path(test_data_dir, 'data')
incremental_mode: false
)!
mut db_metadata := ourdb.new(
path: os.join_path(test_data_dir, 'metadata')
incremental_mode: false
)!
// Create VFS with separate databases for data and metadata
mut vfs := new(mut db_data, mut db_metadata)!
return vfs, test_data_dir
}
fn teardown_vfs(data_dir string) {
os.rmdir_all(data_dir) or {}
}
fn test_get_next_id() ! {
mut vfs, data_dir := setup_vfs()!
defer {
teardown_vfs(data_dir)
}
// Test that get_next_id increments correctly
assert vfs.last_inserted_id == 0
id1 := vfs.get_next_id()
assert id1 == 1
assert vfs.last_inserted_id == 1
id2 := vfs.get_next_id()
assert id2 == 2
assert vfs.last_inserted_id == 2
id3 := vfs.get_next_id()
assert id3 == 3
assert vfs.last_inserted_id == 3
}
fn test_save_load_entry() ! {
mut vfs, data_dir := setup_vfs()!
defer {
teardown_vfs(data_dir)
}
// Create a directory entry
mut dir := Directory{
metadata: Metadata{
id: 1
name: 'test_dir'
file_type: .directory
created: 0
modified: 0
}
entries: []
}
// Save the directory
saved_id := vfs.save_entry(dir)!
assert saved_id == 1
// Load the directory
loaded_entry := vfs.load_entry(1)!
// Verify it's the same directory
loaded_dir := loaded_entry as Directory
assert loaded_dir.metadata.id == dir.metadata.id
assert loaded_dir.metadata.name == dir.metadata.name
assert loaded_dir.metadata.file_type == dir.metadata.file_type
}
fn test_save_load_file_with_data() ! {
mut vfs, data_dir := setup_vfs()!
defer {
teardown_vfs(data_dir)
}
// Create a file entry with data
mut file := File{
metadata: Metadata{
id: 2
name: 'test_file.txt'
file_type: .file
created: 0
modified: 0
}
data: 'Hello, World!'
}
// Save the file
saved_id := vfs.save_entry(file)!
assert saved_id == 2
// Load the file
loaded_entry := vfs.load_entry(2)!
// Verify it's the same file with the same data
loaded_file := loaded_entry as File
assert loaded_file.metadata.id == file.metadata.id
assert loaded_file.metadata.name == file.metadata.name
assert loaded_file.metadata.file_type == file.metadata.file_type
assert loaded_file.data == file.data
}
fn test_save_load_file_without_data() ! {
mut vfs, data_dir := setup_vfs()!
defer {
teardown_vfs(data_dir)
}
// Create a file entry without data
mut file := File{
metadata: Metadata{
id: 3
name: 'empty_file.txt'
file_type: .file
created: 0
modified: 0
}
data: ''
}
// Save the file
saved_id := vfs.save_entry(file)!
assert saved_id == 3
// Load the file
loaded_entry := vfs.load_entry(3)!
// Verify it's the same file with empty data
loaded_file := loaded_entry as File
assert loaded_file.metadata.id == file.metadata.id
assert loaded_file.metadata.name == file.metadata.name
assert loaded_file.metadata.file_type == file.metadata.file_type
assert loaded_file.data == ''
}
fn test_save_load_symlink() ! {
mut vfs, data_dir := setup_vfs()!
defer {
teardown_vfs(data_dir)
}
// Create a symlink entry
mut symlink := Symlink{
metadata: Metadata{
id: 4
name: 'test_link'
file_type: .symlink
created: 0
modified: 0
}
target: '/path/to/target'
}
// Save the symlink
saved_id := vfs.save_entry(symlink)!
assert saved_id == 4
// Load the symlink
loaded_entry := vfs.load_entry(4)!
// Verify it's the same symlink
loaded_symlink := loaded_entry as Symlink
assert loaded_symlink.metadata.id == symlink.metadata.id
assert loaded_symlink.metadata.name == symlink.metadata.name
assert loaded_symlink.metadata.file_type == symlink.metadata.file_type
assert loaded_symlink.target == symlink.target
}
fn test_load_nonexistent_entry() ! {
mut vfs, data_dir := setup_vfs()!
defer {
teardown_vfs(data_dir)
}
// Try to load an entry that doesn't exist
if _ := vfs.load_entry(999) {
assert false, 'Expected error when loading non-existent entry'
} else {
assert err.msg() == 'Entry not found'
}
}

View File

@@ -1,4 +1,4 @@
module vfsnested
module vfs_nested
import freeflowuniverse.herolib.vfs.vfs_local
import os

View File

@@ -1,4 +1,4 @@
module vfsnested
module vfs_nested
import freeflowuniverse.herolib.vfs
@@ -33,12 +33,10 @@ fn (self &NestedVFS) find_vfs(path string) !(vfs.VFSImplementation, string) {
mut prefixes := self.vfs_map.keys()
prefixes.sort(a.len > b.len)
println('debugzone ${path} ${prefixes}')
for prefix in prefixes {
if path.starts_with(prefix) {
relative_path := path[prefix.len..]
if relative_path.starts_with('/') {
return self.vfs_map[prefix], relative_path[1..]
}
return self.vfs_map[prefix], relative_path
}
}
@@ -73,10 +71,22 @@ pub fn (mut self NestedVFS) link_delete(path string) ! {
pub fn (mut self NestedVFS) file_create(path string) !vfs.FSEntry {
mut impl, rel_path := self.find_vfs(path)!
return impl.file_create(rel_path)
sub_entry := impl.file_create(rel_path)!
// Find the prefix for this VFS implementation
mut prefix := ''
for p, v in self.vfs_map {
if v == impl {
prefix = p
break
}
}
return self.nester_entry(sub_entry, prefix)
}
pub fn (mut self NestedVFS) file_read(path string) ![]u8 {
println('debuzone- File read ${path}')
mut impl, rel_path := self.find_vfs(path)!
return impl.file_read(rel_path)
}
@@ -93,7 +103,18 @@ pub fn (mut self NestedVFS) file_delete(path string) ! {
pub fn (mut self NestedVFS) dir_create(path string) !vfs.FSEntry {
mut impl, rel_path := self.find_vfs(path)!
return impl.dir_create(rel_path)
sub_entry := impl.dir_create(rel_path)!
// Find the prefix for this VFS implementation
mut prefix := ''
for p, v in self.vfs_map {
if v == impl {
prefix = p
break
}
}
return self.nester_entry(sub_entry, prefix)
}
pub fn (mut self NestedVFS) dir_list(path string) ![]vfs.FSEntry {
@@ -119,7 +140,24 @@ pub fn (mut self NestedVFS) dir_list(path string) ![]vfs.FSEntry {
}
mut impl, rel_path := self.find_vfs(path)!
return impl.dir_list(rel_path)
sub_entries := impl.dir_list(rel_path)!
// Find the prefix for this VFS implementation
mut prefix := ''
for p, v in self.vfs_map {
if v == impl {
prefix = p
break
}
}
// Convert all entries to nested entries
mut entries := []vfs.FSEntry{}
for sub_entry in sub_entries {
entries << self.nester_entry(sub_entry, prefix)
}
return entries
}
pub fn (mut self NestedVFS) dir_delete(path string) ! {
@@ -141,7 +179,29 @@ pub fn (mut self NestedVFS) get(path string) !vfs.FSEntry {
return self.root_get()
}
mut impl, rel_path := self.find_vfs(path)!
return impl.get(rel_path)
// now must convert entry of nested fvs to entry of nester
sub_entry := impl.get(rel_path)!
// Find the prefix for this VFS implementation
mut prefix := ''
for p, v in self.vfs_map {
if v == impl {
prefix = p
break
}
}
return self.nester_entry(sub_entry, prefix)
}
// nester_entry converts an FSEntry from a sub VFS to an FSEntry for the nester VFS
// by prefixing the nested VFS's path onto the FSEntry's path
fn (self &NestedVFS) nester_entry(entry vfs.FSEntry, prefix string) vfs.FSEntry {
return &NestedEntry{
original: entry
prefix: prefix
}
}
pub fn (mut self NestedVFS) rename(old_path string, new_path string) !vfs.FSEntry {
@@ -153,7 +213,17 @@ pub fn (mut self NestedVFS) rename(old_path string, new_path string) !vfs.FSEntr
}
renamed_file := old_impl.rename(old_rel_path, new_rel_path)!
return renamed_file
// Find the prefix for this VFS implementation
mut prefix := ''
for p, v in self.vfs_map {
if v == old_impl {
prefix = p
break
}
}
return self.nester_entry(renamed_file, prefix)
}
pub fn (mut self NestedVFS) copy(src_path string, dst_path string) !vfs.FSEntry {
@@ -161,7 +231,18 @@ pub fn (mut self NestedVFS) copy(src_path string, dst_path string) !vfs.FSEntry
mut dst_impl, dst_rel_path := self.find_vfs(dst_path)!
if src_impl == dst_impl {
return src_impl.copy(src_rel_path, dst_rel_path)
copied_file := src_impl.copy(src_rel_path, dst_rel_path)!
// Find the prefix for this VFS implementation
mut prefix := ''
for p, v in self.vfs_map {
if v == src_impl {
prefix = p
break
}
}
return self.nester_entry(copied_file, prefix)
}
// Copy across different VFS implementations
@@ -169,18 +250,50 @@ pub fn (mut self NestedVFS) copy(src_path string, dst_path string) !vfs.FSEntry
data := src_impl.file_read(src_rel_path)!
new_file := dst_impl.file_create(dst_rel_path)!
dst_impl.file_write(dst_rel_path, data)!
return new_file
// Find the prefix for the destination VFS implementation
mut prefix := ''
for p, v in self.vfs_map {
if v == dst_impl {
prefix = p
break
}
}
return self.nester_entry(new_file, prefix)
}
pub fn (mut self NestedVFS) move(src_path string, dst_path string) !vfs.FSEntry {
mut src_impl, src_rel_path := self.find_vfs(src_path)!
_, dst_rel_path := self.find_vfs(dst_path)!
return src_impl.move(src_rel_path, dst_rel_path)
moved_file := src_impl.move(src_rel_path, dst_rel_path)!
// Find the prefix for this VFS implementation
mut prefix := ''
for p, v in self.vfs_map {
if v == src_impl {
prefix = p
break
}
}
return self.nester_entry(moved_file, prefix)
}
pub fn (mut self NestedVFS) link_create(target_path string, link_path string) !vfs.FSEntry {
mut impl, rel_path := self.find_vfs(link_path)!
return impl.link_create(target_path, rel_path)
link_entry := impl.link_create(target_path, rel_path)!
// Find the prefix for this VFS implementation
mut prefix := ''
for p, v in self.vfs_map {
if v == impl {
prefix = p
break
}
}
return self.nester_entry(link_entry, prefix)
}
pub fn (mut self NestedVFS) link_read(path string) !string {
@@ -250,3 +363,37 @@ pub fn (self &MountEntry) is_file() bool {
pub fn (self &MountEntry) is_symlink() bool {
return self.metadata.file_type == .symlink
}
// NestedEntry wraps an FSEntry from a sub VFS and prefixes its path
pub struct NestedEntry {
pub mut:
original vfs.FSEntry
prefix string
}
fn (e &NestedEntry) get_metadata() vfs.Metadata {
return e.original.get_metadata()
}
fn (e &NestedEntry) get_path() string {
original_path := e.original.get_path()
if original_path == '/' {
return e.prefix
}
return e.prefix + original_path
}
// is_dir returns true if the entry is a directory
pub fn (self &NestedEntry) is_dir() bool {
return self.original.is_dir()
}
// is_file returns true if the entry is a file
pub fn (self &NestedEntry) is_file() bool {
return self.original.is_file()
}
// is_symlink returns true if the entry is a symlink
pub fn (self &NestedEntry) is_symlink() bool {
return self.original.is_symlink()
}