fix vfsimplemetation, add path metadata
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
if file_entry.metadata.id !in args.dst_parent_dir.children {
|
||||
args.dst_parent_dir.children << file_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(file_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 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
|
||||
}
|
||||
|
||||
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(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
|
||||
|
||||
@@ -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('/')
|
||||
|
||||
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]}')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
return self.directory_get_entry(mut current, path) or {
|
||||
return error('Path not found: ${path}')
|
||||
}
|
||||
}
|
||||
|
||||
return FSEntry(current)
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
fn (mut self DatabaseVFS) get_directory(path string) !&Directory {
|
||||
|
||||
@@ -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,10 +33,11 @@ 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 {
|
||||
@@ -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)!
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
// }
|
||||
|
||||
Reference in New Issue
Block a user