isolate vfs's and improve documentation

This commit is contained in:
timurgordon
2025-02-27 11:42:46 +03:00
parent fe1becabaf
commit d06a806184
23 changed files with 551 additions and 1459 deletions

View File

@@ -0,0 +1,48 @@
# Nested Filesystem Implementation (vfs_nested)
A virtual filesystem implementation that allows mounting multiple VFS implementations at different path prefixes, creating a unified filesystem view.
## Features
- Mount multiple VFS implementations
- Path-based routing to appropriate implementations
- Transparent operation across mounted filesystems
- Hierarchical organization
- Cross-implementation file operations
- Virtual root directory showing mount points
## Implementation Details
### Structure
```
vfs_nested/
├── vfsnested.v # Core implementation
└── nested_test.v # Implementation tests
```
### Key Components
- `NestedVFS`: Main implementation struct that manages mounted filesystems
- `RootEntry`: Special entry type representing the root directory
- `MountEntry`: Special entry type representing mounted filesystem points
## Usage
```v
import vfs
import vfs_nested
fn main() ! {
mut nested := vfs_nested.new()
mut local_fs := vfs.new_vfs('local', '/tmp/local')!
nested.add_vfs('/local', local_fs)!
nested.file_create('/local/test.txt')!
}
```
## Limitations
- Cannot rename/move files across different implementations
- Symlinks must be contained within a single implementation
- No atomic operations across implementations
- Mount points are fixed after creation

View File

@@ -0,0 +1,85 @@
module vfsnested
import freeflowuniverse.herolib.vfs.vfs_local
import os
fn test_nested() ! {
println('Testing Nested VFS...')
// Create root directories for test VFS instances
os.mkdir_all('/tmp/test_nested_vfs/vfs1') or { panic(err) }
os.mkdir_all('/tmp/test_nested_vfs/vfs2') or { panic(err) }
os.mkdir_all('/tmp/test_nested_vfs/vfs3') or { panic(err) }
// Create VFS instances
mut vfs1 := vfs_local.new_local_vfs('/tmp/test_nested_vfs/vfs1') or { panic(err) }
mut vfs2 := vfs_local.new_local_vfs('/tmp/test_nested_vfs/vfs2') or { panic(err) }
mut vfs3 := vfs_local.new_local_vfs('/tmp/test_nested_vfs/vfs3') or { panic(err) }
// Create nested VFS
mut nested_vfs := new()
// Add VFS instances at different paths
nested_vfs.add_vfs('/data', vfs1) or { panic(err) }
nested_vfs.add_vfs('/config', vfs2) or { panic(err) }
nested_vfs.add_vfs('/data/backup', vfs3) or { panic(err) } // Nested under /data
println('\nTesting file operations...')
// Create and write to files in different VFS instances
nested_vfs.file_create('/data/test.txt') or { panic(err) }
nested_vfs.file_write('/data/test.txt', 'Hello from VFS1'.bytes()) or { panic(err) }
nested_vfs.file_create('/config/settings.txt') or { panic(err) }
nested_vfs.file_write('/config/settings.txt', 'Hello from VFS2'.bytes()) or { panic(err) }
nested_vfs.file_create('/data/backup/archive.txt') or { panic(err) }
nested_vfs.file_write('/data/backup/archive.txt', 'Hello from VFS3'.bytes()) or { panic(err) }
// Read and verify file contents
data1 := nested_vfs.file_read('/data/test.txt') or { panic(err) }
println('Content from /data/test.txt: ${data1.bytestr()}')
data2 := nested_vfs.file_read('/config/settings.txt') or { panic(err) }
println('Content from /config/settings.txt: ${data2.bytestr()}')
data3 := nested_vfs.file_read('/data/backup/archive.txt') or { panic(err) }
println('Content from /data/backup/archive.txt: ${data3.bytestr()}')
println('\nTesting directory operations...')
// List root directory
println('Root directory contents:')
root_entries := nested_vfs.dir_list('/') or { panic(err) }
for entry in root_entries {
meta := entry.get_metadata()
println('- ${meta.name} (${meta.file_type})')
}
// Create and list directories
nested_vfs.dir_create('/data/subdir') or { panic(err) }
nested_vfs.file_create('/data/subdir/file.txt') or { panic(err) }
nested_vfs.file_write('/data/subdir/file.txt', 'Nested file content'.bytes()) or { panic(err) }
println('\nListing /data directory:')
data_entries := nested_vfs.dir_list('/data') or { panic(err) }
for entry in data_entries {
meta := entry.get_metadata()
println('- ${meta.name} (${meta.file_type})')
}
println('\nTesting cross-VFS operations...')
// Copy file between different VFS instances
nested_vfs.copy('/data/test.txt', '/config/test_copy.txt') or { panic(err) }
copy_data := nested_vfs.file_read('/config/test_copy.txt') or { panic(err) }
println('Copied file content: ${copy_data.bytestr()}')
println('\nCleanup...')
// Cleanup
nested_vfs.destroy() or { panic(err) }
os.rmdir_all('/tmp/test_nested_vfs') or { panic(err) }
println('Test completed successfully!')
}

View File

@@ -0,0 +1,252 @@
module vfsnested
import freeflowuniverse.herolib.vfs
// NestedVFS represents a VFS that can contain multiple nested VFS instances
pub struct NestedVFS {
mut:
vfs_map map[string]vfs.VFSImplementation @[skip] // Map of path prefixes to VFS implementations
}
// new creates a new NestedVFS instance
pub fn new() &NestedVFS {
return &NestedVFS{
vfs_map: map[string]vfs.VFSImplementation{}
}
}
// add_vfs adds a new VFS implementation at the specified path prefix
pub fn (mut self NestedVFS) add_vfs(prefix string, impl vfs.VFSImplementation) ! {
if prefix in self.vfs_map {
return error('VFS already exists at prefix: ${prefix}')
}
self.vfs_map[prefix] = impl
}
// find_vfs finds the appropriate VFS implementation for a given path
fn (self &NestedVFS) find_vfs(path string) !(vfs.VFSImplementation, string) {
if path == '' || path == '/' {
return self, '/'
}
// Sort prefixes by length (longest first) to match most specific path
mut prefixes := self.vfs_map.keys()
prefixes.sort(a.len > b.len)
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
}
}
return error('No VFS found for path: ${path}')
}
// Implementation of VFSImplementation interface
pub fn (mut self NestedVFS) root_get() !vfs.FSEntry {
// Return a special root entry that represents the nested VFS
return &RootEntry{
metadata: vfs.Metadata{
id: 0
name: ''
file_type: .directory
size: 0
created_at: 0
modified_at: 0
accessed_at: 0
}
}
}
pub fn (mut self NestedVFS) delete(path string) ! {
mut impl, rel_path := self.find_vfs(path)!
return impl.delete(rel_path)
}
pub fn (mut self NestedVFS) link_delete(path string) ! {
mut impl, rel_path := self.find_vfs(path)!
return impl.link_delete(rel_path)
}
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)
}
pub fn (mut self NestedVFS) file_read(path string) ![]u8 {
mut impl, rel_path := self.find_vfs(path)!
return impl.file_read(rel_path)
}
pub fn (mut self NestedVFS) file_write(path string, data []u8) ! {
mut impl, rel_path := self.find_vfs(path)!
return impl.file_write(rel_path, data)
}
pub fn (mut self NestedVFS) file_delete(path string) ! {
mut impl, rel_path := self.find_vfs(path)!
return impl.file_delete(rel_path)
}
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)
}
pub fn (mut self NestedVFS) dir_list(path string) ![]vfs.FSEntry {
// Special case for root directory
if path == '' || path == '/' {
mut entries := []vfs.FSEntry{}
for prefix, mut impl in self.vfs_map {
root := impl.root_get() or { continue }
entries << &MountEntry{
metadata: vfs.Metadata{
id: 0
name: prefix
file_type: .directory
size: 0
created_at: 0
modified_at: 0
accessed_at: 0
}
impl: impl
}
}
return entries
}
mut impl, rel_path := self.find_vfs(path)!
return impl.dir_list(rel_path)
}
pub fn (mut self NestedVFS) dir_delete(path string) ! {
mut impl, rel_path := self.find_vfs(path)!
return impl.dir_delete(rel_path)
}
pub fn (mut self NestedVFS) exists(path string) bool {
// QUESTION: should root be nestervfs's own?
if path == '' || path == '/' {
return true
}
mut impl, rel_path := self.find_vfs(path) or { return false }
return impl.exists(rel_path)
}
pub fn (mut self NestedVFS) get(path string) !vfs.FSEntry {
if path == '' || path == '/' {
return self.root_get()
}
mut impl, rel_path := self.find_vfs(path)!
return impl.get(rel_path)
}
pub fn (mut self NestedVFS) rename(old_path string, new_path string) !vfs.FSEntry {
mut old_impl, old_rel_path := self.find_vfs(old_path)!
mut new_impl, new_rel_path := self.find_vfs(new_path)!
if old_impl != new_impl {
return error('Cannot rename across different VFS implementations')
}
renamed_file := old_impl.rename(old_rel_path, new_rel_path)!
return renamed_file
}
pub fn (mut self NestedVFS) copy(src_path string, dst_path string) !vfs.FSEntry {
mut src_impl, src_rel_path := self.find_vfs(src_path)!
mut dst_impl, dst_rel_path := self.find_vfs(dst_path)!
if src_impl == dst_impl {
return src_impl.copy(src_rel_path, dst_rel_path)
}
// Copy 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)!
new_file := dst_impl.file_create(dst_rel_path)!
dst_impl.file_write(dst_rel_path, data)!
return new_file
}
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)
}
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)
}
pub fn (mut self NestedVFS) link_read(path string) !string {
mut impl, rel_path := self.find_vfs(path)!
return impl.link_read(rel_path)
}
pub fn (mut self NestedVFS) destroy() ! {
for _, mut impl in self.vfs_map {
impl.destroy()!
}
}
// Special entry types for the nested VFS
struct RootEntry {
metadata vfs.Metadata
}
fn (e &RootEntry) get_metadata() vfs.Metadata {
return e.metadata
}
fn (e &RootEntry) get_path() string {
return '/'
}
// is_dir returns true if the entry is a directory
pub fn (self &RootEntry) is_dir() bool {
return self.metadata.file_type == .directory
}
// is_file returns true if the entry is a file
pub fn (self &RootEntry) is_file() bool {
return self.metadata.file_type == .file
}
// is_symlink returns true if the entry is a symlink
pub fn (self &RootEntry) is_symlink() bool {
return self.metadata.file_type == .symlink
}
pub struct MountEntry {
pub mut:
metadata vfs.Metadata
impl vfs.VFSImplementation
}
fn (e &MountEntry) get_metadata() vfs.Metadata {
return e.metadata
}
fn (e &MountEntry) get_path() string {
return '/${e.metadata.name.trim_left('/')}'
}
// is_dir returns true if the entry is a directory
pub fn (self &MountEntry) is_dir() bool {
return self.metadata.file_type == .directory
}
// is_file returns true if the entry is a file
pub fn (self &MountEntry) is_file() bool {
return self.metadata.file_type == .file
}
// is_symlink returns true if the entry is a symlink
pub fn (self &MountEntry) is_symlink() bool {
return self.metadata.file_type == .symlink
}