Files
herolib/lib/vfs/vfs_nested/vfsnested.v
2025-10-12 12:30:19 +03:00

464 lines
11 KiB
V

module vfs_nested
import incubaid.herolib.vfs
import time
// 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..]
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: ''
path: '/'
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)!
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 {
// Special handling for macOS resource fork files (._*)
if path.starts_with('/._') || path.contains('/._') {
// Return empty data for resource fork files
return []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)!
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 {
// 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
path: 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)!
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) ! {
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
}
// // Special handling for macOS resource fork files (._*)
// if path.starts_with('/._') || path.contains('/._') {
// return true // Pretend these files exist for WebDAV Class 2 compatibility
// }
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()
}
// // Special handling for macOS resource fork files (._*)
// if path.starts_with('/._') || path.contains('/._') {
// // Extract the filename from the path
// filename := path.all_after_last('/')
// // Create a dummy resource fork entry
// return &ResourceForkEntry{
// metadata: vfs.Metadata{
// id: 0
// name: filename
// file_type: .file
// size: 0
// created_at: time.now().unix()
// modified_at: time.now().unix()
// accessed_at: time.now().unix()
// }
// path: path
// }
// }
mut impl, rel_path := self.find_vfs(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 {
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)!
// 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 {
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 {
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
// 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)!
// 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)!
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)!
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 {
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
}
// 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.trim_string_left('/')}'
}
// 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()
}
// // ResourceForkEntry represents a macOS resource fork file (._*)
// pub struct ResourceForkEntry {
// pub mut:
// metadata vfs.Metadata
// path string
// }
// fn (e &ResourceForkEntry) get_metadata() vfs.Metadata {
// return e.metadata
// }
// fn (e &ResourceForkEntry) get_path() string {
// return e.path
// }
// // is_dir returns true if the entry is a directory
// pub fn (self &ResourceForkEntry) is_dir() bool {
// return false
// }
// // is_file returns true if the entry is a file
// pub fn (self &ResourceForkEntry) is_file() bool {
// return true
// }
// // is_symlink returns true if the entry is a symlink
// pub fn (self &ResourceForkEntry) is_symlink() bool {
// return false
// }