fix vfsimplemetation, add path metadata

This commit is contained in:
timurgordon
2025-03-06 01:23:17 +01:00
parent ae7e7ecb84
commit b01e40da40
10 changed files with 179 additions and 71 deletions

View File

@@ -7,6 +7,7 @@ pub struct Metadata {
pub mut:
id u32 @[required] // unique identifier used as key in DB
name string @[required] // name of file or directory
path string @[required] // path of file or directory
file_type FileType
size u64
created_at i64 // unix epoch timestamp

View File

@@ -7,6 +7,7 @@ import freeflowuniverse.herolib.vfs
fn encode_metadata(mut e encoder.Encoder, m vfs.Metadata) {
e.add_u32(m.id)
e.add_string(m.name)
e.add_string(m.path)
e.add_u8(u8(m.file_type)) // FileType enum as u8
e.add_u64(m.size)
e.add_i64(m.created_at)
@@ -21,6 +22,7 @@ fn encode_metadata(mut e encoder.Encoder, m vfs.Metadata) {
fn decode_metadata(mut d encoder.Decoder) !vfs.Metadata {
id := d.get_u32()!
name := d.get_string()!
path := d.get_string()!
file_type_byte := d.get_u8()!
size := d.get_u64()!
created_at := d.get_i64()!
@@ -33,6 +35,7 @@ fn decode_metadata(mut d encoder.Decoder) !vfs.Metadata {
return vfs.Metadata{
id: id
name: name
path: path
file_type: unsafe { vfs.FileType(file_type_byte) }
size: size
created_at: created_at

View File

@@ -7,6 +7,7 @@ import freeflowuniverse.herolib.vfs
pub struct NewMetadata {
pub mut:
name string @[required] // name of file or directory
path string @[required] // name of file or directory
file_type vfs.FileType @[required]
size u64 @[required]
mode u32 = 0o644 // file permissions
@@ -18,6 +19,7 @@ pub fn (mut fs DatabaseVFS) new_metadata(metadata NewMetadata) vfs.Metadata {
return vfs.new_metadata(
id: fs.get_next_id()
name: metadata.name
path: metadata.path
file_type: metadata.file_type
size: metadata.size
mode: metadata.mode

View File

@@ -15,7 +15,7 @@ fn (d &Directory) get_metadata() vfs.Metadata {
}
fn (d &Directory) get_path() string {
return d.metadata.name
return '/${d.metadata.name.trim_string_left('/')}'
}
// is_dir returns true if the entry is a directory

View File

@@ -32,7 +32,7 @@ fn (f &File) get_metadata() vfs.Metadata {
}
fn (f &File) get_path() string {
return f.metadata.name
return '/${f.metadata.name.trim_string_left('/')}'
}
// is_dir returns true if the entry is a directory
@@ -53,6 +53,7 @@ pub fn (f &File) is_symlink() bool {
pub struct NewFile {
pub:
name string @[required] // name of file or directory
path string @[required] // path of file or directory
data string
mode u32 = 0o644 // file permissions
owner string = 'user'
@@ -67,6 +68,7 @@ pub fn (mut fs DatabaseVFS) new_file(file NewFile) !&File {
parent_id: file.parent_id
metadata: fs.new_metadata(NewMetadata{
name: file.name
path: file.path
mode: file.mode
owner: file.owner
group: file.group
@@ -85,6 +87,7 @@ pub fn (mut fs DatabaseVFS) copy_file(file File) !&File {
return fs.new_file(
data: file.data
name: file.metadata.name
path: file.metadata.path
mode: file.metadata.mode
owner: file.metadata.owner
group: file.metadata.group

View File

@@ -10,7 +10,7 @@ fn (e &FSEntry) get_metadata() vfs.Metadata {
}
fn (e &FSEntry) get_path() string {
return e.metadata.name
return e.metadata.path
}
fn (e &FSEntry) is_dir() bool {

View File

@@ -4,7 +4,8 @@ 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 {
pub fn (mut fs DatabaseVFS) directory_mkdir(mut dir Directory, name_ string) !&Directory {
name := name_.trim('/')
// Check if directory already exists
for child_id in dir.children {
if entry := fs.load_entry(child_id) {
@@ -14,7 +15,18 @@ pub fn (mut fs DatabaseVFS) directory_mkdir(mut dir Directory, name string) !&Di
}
}
new_dir := fs.new_directory(name: name, parent_id: dir.metadata.id)!
path := if dir.metadata.path == '/' {
'/${name}'
} else {
"${dir.metadata.path.trim('/')}/${name}"
}
new_dir := fs.new_directory(
name: name,
path: path
parent_id: dir.metadata.id
)!
println('new_dit ${new_dir}')
dir.children << new_dir.metadata.id
fs.save_entry(dir)!
return new_dir
@@ -23,6 +35,7 @@ pub fn (mut fs DatabaseVFS) directory_mkdir(mut dir Directory, name string) !&Di
pub struct NewDirectory {
pub:
name string @[required] // name of file or directory
path string @[required] // name of file or directory
mode u32 = 0o755 // file permissions
owner string = 'user'
group string = 'user'
@@ -36,6 +49,7 @@ pub fn (mut fs DatabaseVFS) new_directory(dir NewDirectory) !&Directory {
parent_id: dir.parent_id
metadata: fs.new_metadata(NewMetadata{
name: dir.name
path: dir.path
mode: dir.mode
owner: dir.owner
group: dir.group
@@ -75,7 +89,8 @@ pub fn (mut fs DatabaseVFS) copy_directory(dir Directory) !&Directory {
}
// touch creates a new empty file with default permissions
pub fn (mut fs DatabaseVFS) directory_touch(dir_ Directory, name string) !&File {
pub fn (mut fs DatabaseVFS) directory_touch(dir_ Directory, name_ string) !&File {
name := name_.trim('/')
mut dir := dir_
// First, make sure we're working with the latest version of the directory
@@ -98,6 +113,7 @@ pub fn (mut fs DatabaseVFS) directory_touch(dir_ Directory, name string) !&File
mut new_file := fs.new_file(
parent_id: dir.metadata.id
name: name
path: "${dir.metadata.path.trim('/')}/${name}"
)!
// Ensure parent_id is set correctly
@@ -199,44 +215,60 @@ pub fn (mut fs DatabaseVFS) directory_move(dir_ Directory, args_ MoveDirArgs) !&
for child_id in dir.children {
if mut entry := fs.load_entry(child_id) {
if entry.metadata.name == args.src_entry_name {
if entry is File {
return error('${args.src_entry_name} is a file')
}
if entry is Symlink {
return error('${args.src_entry_name} is a symlink')
}
found = true
child_id_to_remove = child_id
mut entry_ := entry as Directory
entry_.metadata.name = args.dst_entry_name
entry_.metadata.modified_at = time.now().unix()
entry_.parent_id = args.dst_parent_dir.metadata.id
// Recursively update all child paths in moved directory
fs.move_children_recursive(mut entry_)!
// Handle both files and directories
if entry is File {
mut file_entry := entry as File
file_entry.metadata.name = args.dst_entry_name
file_entry.metadata.path = "/${args.dst_parent_dir.metadata.path.trim('/')}/${args.dst_entry_name}"
file_entry.metadata.modified_at = time.now().unix()
file_entry.parent_id = args.dst_parent_dir.metadata.id
// Ensure no duplicate entries in dst_parent_dir
if entry_.metadata.id !in args.dst_parent_dir.children {
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
// Ensure no duplicate entries in dst_parent_dir
if file_entry.metadata.id !in args.dst_parent_dir.children {
args.dst_parent_dir.children << file_entry.metadata.id
}
}
return &entry_
// 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(file_entry)!
fs.save_entry(args.dst_parent_dir)!
// Return the destination directory
return args.dst_parent_dir
} else {
// Handle directory
mut dir_entry := entry as Directory
dir_entry.metadata.name = args.dst_entry_name
dir_entry.metadata.path = "${args.dst_parent_dir.metadata.path.trim_string_right('/')}/${args.dst_entry_name}"
dir_entry.metadata.modified_at = time.now().unix()
dir_entry.parent_id = args.dst_parent_dir.metadata.id
// Recursively update all child paths in moved directory
fs.move_children_recursive(mut dir_entry)!
// Ensure no duplicate entries in dst_parent_dir
if dir_entry.metadata.id !in args.dst_parent_dir.children {
args.dst_parent_dir.children << dir_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(dir_entry)!
fs.save_entry(args.dst_parent_dir)!
return &dir_entry
}
}
}
}
@@ -418,6 +450,7 @@ pub fn (mut fs DatabaseVFS) directory_rename(dir Directory, src_name string, dst
found = true
mut dir_entry := entry as Directory
dir_entry.metadata.name = dst_name
dir_entry.metadata.path = "${dir_entry.metadata.path.all_before_last('/')}/dst_name"
dir_entry.metadata.modified_at = time.now().unix()
fs.save_entry(dir_entry)!
return &dir_entry

View File

@@ -22,6 +22,7 @@ pub fn (mut fs DatabaseVFS) root_get_as_dir() !&Directory {
id: fs.get_next_id()
file_type: .directory
name: ''
path: '/'
created_at: time.now().unix()
modified_at: time.now().unix()
accessed_at: time.now().unix()
@@ -35,42 +36,34 @@ pub fn (mut fs DatabaseVFS) root_get_as_dir() !&Directory {
return &myroot
}
fn (mut self DatabaseVFS) get_entry(path string) !FSEntry {
fn (mut self DatabaseVFS) get_entry(path_ string) !FSEntry {
path := '/${path_.trim_left('/').trim_right('/')}'
if path == '/' || path == '' || path == '.' {
return FSEntry(self.root_get_as_dir()!)
}
mut current := *self.root_get_as_dir()!
parts := path.trim_left('/').split('/')
return self.directory_get_entry(mut current, path) or {
return error('Path not found: ${path}')
}
}
for i := 0; i < parts.len; i++ {
mut found := false
children := self.directory_children(mut current, false)!
for child in children {
if child.metadata.name == parts[i] {
match child {
Directory {
current = child
found = true
break
}
else {
if i == parts.len - 1 {
return child
} else {
return error('Not a directory: ${parts[i]}')
}
}
}
fn (mut self DatabaseVFS) directory_get_entry(mut dir Directory, path string) ?FSEntry {
mut children := self.directory_children(mut dir, false) or {
panic('this should never happen')
}
for mut child in children {
println('debugzoni1 ${child.metadata.path} ${path}')
if child.metadata.path == path {
return child
} else if child is Directory {
mut child_dir := child as Directory
return self.directory_get_entry(mut child_dir, path) or {
continue
}
}
if !found {
return error('Path not found: ${path}')
}
}
return FSEntry(current)
return none
}
fn (mut self DatabaseVFS) get_directory(path string) !&Directory {

View File

@@ -11,7 +11,8 @@ pub fn (mut fs DatabaseVFS) root_get() !vfs.FSEntry {
return fs.root_get_as_dir()!
}
pub fn (mut self DatabaseVFS) file_create(path string) !vfs.FSEntry {
pub fn (mut self DatabaseVFS) file_create(path_ string) !vfs.FSEntry {
path := '/${path_.trim_left('/').trim_right('/')}'
log.info('[DatabaseVFS] Creating file ${path}')
// Get parent directory
parent_path := os.dir(path)
@@ -21,7 +22,8 @@ pub fn (mut self DatabaseVFS) file_create(path string) !vfs.FSEntry {
return self.directory_touch(parent_dir, file_name)!
}
pub fn (mut self DatabaseVFS) file_read(path string) ![]u8 {
pub fn (mut self DatabaseVFS) file_read(path_ string) ![]u8 {
path := '/${path_.trim_left('/').trim_right('/')}'
log.info('[DatabaseVFS] Reading file ${path}')
mut entry := self.get_entry(path)!
if mut entry is File {
@@ -31,16 +33,17 @@ pub fn (mut self DatabaseVFS) file_read(path string) ![]u8 {
}
pub fn (mut self DatabaseVFS) file_write(path string, data []u8) ! {
self.print()!
if mut entry := self.get_entry(path) {
println(entry)
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 {
} else {
self.file_create(path)!
self.file_write(path, data)!
}
@@ -54,13 +57,13 @@ pub fn (mut self DatabaseVFS) file_delete(path string) ! {
self.directory_rm(mut parent_dir, file_name)!
}
pub fn (mut self DatabaseVFS) dir_create(path string) !vfs.FSEntry {
pub fn (mut self DatabaseVFS) dir_create(path_ string) !vfs.FSEntry {
path := '/${path_.trim_left('/').trim_right('/')}'
log.info('[DatabaseVFS] Creating Directory ${path}')
parent_path := os.dir(path)
dir_name := os.base(path)
file_name := os.base(path)
mut parent_dir := self.get_directory(parent_path)!
return self.directory_mkdir(mut parent_dir, dir_name)!
return self.directory_mkdir(mut parent_dir, file_name)!
}
pub fn (mut self DatabaseVFS) dir_list(path string) ![]vfs.FSEntry {
@@ -97,6 +100,7 @@ pub fn (mut self DatabaseVFS) link_create(target_path string, link_path string)
metadata: vfs.Metadata{
id: self.get_next_id()
name: link_name
path: link_path
file_type: .symlink
created_at: time.now().unix()
modified_at: time.now().unix()
@@ -130,6 +134,8 @@ pub fn (mut self DatabaseVFS) link_delete(path string) ! {
}
pub fn (mut self DatabaseVFS) exists(path_ string) bool {
println('debugznoiki')
self.print() or {panic(err.msg())}
path := if !path_.starts_with('/') {
'/${path_}'
} else {
@@ -143,6 +149,7 @@ pub fn (mut self DatabaseVFS) exists(path_ string) bool {
}
pub fn (mut fs DatabaseVFS) get(path string) !vfs.FSEntry {
log.info('[DatabaseVFS] Getting filesystem entry ${path}')
return fs.get_entry(path)!
}

View File

@@ -1,6 +1,7 @@
module vfs_nested
import freeflowuniverse.herolib.vfs
import time
// NestedVFS represents a VFS that can contain multiple nested VFS instances
pub struct NestedVFS {
@@ -33,7 +34,6 @@ 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..]
@@ -50,6 +50,7 @@ pub fn (mut self NestedVFS) root_get() !vfs.FSEntry {
metadata: vfs.Metadata{
id: 0
name: ''
path: '/'
file_type: .directory
size: 0
created_at: 0
@@ -87,6 +88,13 @@ pub fn (mut self NestedVFS) file_create(path string) !vfs.FSEntry {
pub fn (mut self NestedVFS) file_read(path string) ![]u8 {
println('debuzone- File read ${path}')
// 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)
}
@@ -127,6 +135,7 @@ pub fn (mut self NestedVFS) dir_list(path string) ![]vfs.FSEntry {
metadata: vfs.Metadata{
id: 0
name: prefix
path: prefix
file_type: .directory
size: 0
created_at: 0
@@ -170,6 +179,12 @@ pub fn (mut self NestedVFS) exists(path string) bool {
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)
}
@@ -178,6 +193,27 @@ 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
@@ -380,7 +416,7 @@ fn (e &NestedEntry) get_path() string {
if original_path == '/' {
return e.prefix
}
return e.prefix + original_path
return e.prefix + '/${original_path.trim_string_left("/")}'
}
// is_dir returns true if the entry is a directory
@@ -397,3 +433,33 @@ pub fn (self &NestedEntry) is_file() bool {
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
// }