Files
herolib/lib/vfs/vfsourdb/vfsourdb.v
Mahmoud Emad 7b453962ca feat: Enhance WebDAV server and add VFS encoder/decoder tests
- Add user authentication to the WebDAV server using a user
  database.
- Implement encoding and decoding functionality for directories,
  files, and symlinks in the OurDBFS VFS.
- Add comprehensive unit tests for the encoder and decoder
  functions.
- Improve the OurDBFS factory method to handle directory creation
  more robustly using pathlib.
- Add `delete` and `link_delete` methods to the `NestedVFS` and
  `OurDBVFS` implementations (though currently unimplemented).
- Improve WebDAV file handling to correctly determine and set the
  content type.  The previous implementation was incomplete and
  returned a dummy response.
- Update VFS test to actually test functionality.
- Remove unnecessary `root_dir` parameter from the WebDAV app.
2025-02-18 17:40:37 +02:00

312 lines
7.3 KiB
V

module vfsourdb
import freeflowuniverse.herolib.vfs.vfscore
import freeflowuniverse.herolib.vfs.ourdb_fs
import os
import time
// OurDBVFS represents a VFS that uses OurDB as the underlying storage
pub struct OurDBVFS {
mut:
core &ourdb_fs.OurDBFS
}
// new creates a new OurDBVFS instance
pub fn new(data_dir string, metadata_dir string) !&OurDBVFS {
mut core := ourdb_fs.new(
data_dir: data_dir
metadata_dir: metadata_dir
incremental_mode: true
)!
return &OurDBVFS{
core: core
}
}
// Implementation of VFSImplementation interface
pub fn (mut self OurDBVFS) root_get() !vfscore.FSEntry {
mut root := self.core.get_root()!
return convert_to_vfscore_entry(root)
}
pub fn (mut self OurDBVFS) file_create(path string) !vfscore.FSEntry {
// Get parent directory
parent_path := os.dir(path)
file_name := os.base(path)
println('file path: ${path}')
println('parent_path: ${parent_path}')
mut parent_dir := self.get_directory(parent_path)!
println('parent_dir file: ${parent_dir}')
mut file := parent_dir.touch(file_name)!
return convert_to_vfscore_entry(file)
}
pub fn (mut self OurDBVFS) file_read(path string) ![]u8 {
mut entry := self.get_entry(path)!
if mut entry is ourdb_fs.File {
content := entry.read()!
return content.bytes()
}
return error('Not a file: ${path}')
}
pub fn (mut self OurDBVFS) file_write(path string, data []u8) ! {
mut entry := self.get_entry(path)!
println('file_write - entry type: ${typeof(entry).name}')
if mut entry is ourdb_fs.File {
entry.write(data.bytestr())!
} else {
return error('Not a file: ${path}')
}
}
pub fn (mut self OurDBVFS) delete(path string) ! {
// mut impl, rel_path := self.find_vfs(path)!
// return impl.file_read(rel_path)
}
pub fn (mut self OurDBVFS) link_delete(path string) ! {
// mut impl, rel_path := self.find_vfs(path)!
// return impl.file_read(rel_path)
}
pub fn (mut self OurDBVFS) file_delete(path string) ! {
parent_path := os.dir(path)
file_name := os.base(path)
mut parent_dir := self.get_directory(parent_path)!
parent_dir.rm(file_name)!
}
pub fn (mut self OurDBVFS) dir_create(path string) !vfscore.FSEntry {
parent_path := os.dir(path)
dir_name := os.base(path)
mut parent_dir := self.get_directory(parent_path)!
println('parent_dir: ${parent_dir}')
println('dir_name: ${dir_name}')
mut new_dir := parent_dir.mkdir(dir_name)!
println('new_dir: ${new_dir}')
new_dir.save()! // Ensure the directory is saved
return convert_to_vfscore_entry(new_dir)
}
pub fn (mut self OurDBVFS) dir_list(path string) ![]vfscore.FSEntry {
mut dir := self.get_directory(path)!
mut entries := dir.children(false)!
mut result := []vfscore.FSEntry{}
for entry in entries {
result << convert_to_vfscore_entry(entry)
}
return result
}
pub fn (mut self OurDBVFS) dir_delete(path string) ! {
parent_path := os.dir(path)
dir_name := os.base(path)
mut parent_dir := self.get_directory(parent_path)!
parent_dir.rm(dir_name)!
}
pub fn (mut self OurDBVFS) exists(path string) bool {
if path == '/' {
return true
}
self.get_entry(path) or { return false }
return true
}
pub fn (mut self OurDBVFS) get(path string) !vfscore.FSEntry {
mut entry := self.get_entry(path)!
return convert_to_vfscore_entry(entry)
}
pub fn (mut self OurDBVFS) rename(old_path string, new_path string) ! {
return error('Not implemented')
}
pub fn (mut self OurDBVFS) copy(src_path string, dst_path string) ! {
return error('Not implemented')
}
pub fn (mut self OurDBVFS) link_create(target_path string, link_path string) !vfscore.FSEntry {
parent_path := os.dir(link_path)
link_name := os.base(link_path)
mut parent_dir := self.get_directory(parent_path)!
mut symlink := ourdb_fs.Symlink{
metadata: ourdb_fs.Metadata{
id: u32(time.now().unix())
name: link_name
file_type: .symlink
created_at: time.now().unix()
modified_at: time.now().unix()
accessed_at: time.now().unix()
mode: 0o777
owner: 'user'
group: 'user'
}
target: target_path
parent_id: parent_dir.metadata.id
myvfs: self.core
}
parent_dir.add_symlink(mut symlink)!
return convert_to_vfscore_entry(symlink)
}
pub fn (mut self OurDBVFS) link_read(path string) !string {
mut entry := self.get_entry(path)!
if mut entry is ourdb_fs.Symlink {
return entry.get_target()!
}
return error('Not a symlink: ${path}')
}
pub fn (mut self OurDBVFS) destroy() ! {
// Nothing to do as the core VFS handles cleanup
}
fn (mut self OurDBVFS) get_entry(path string) !ourdb_fs.FSEntry {
if path == '/' {
return ourdb_fs.FSEntry(self.core.get_root()!)
}
mut current := *self.core.get_root()!
parts := path.trim_left('/').split('/')
println('Traversing path: ${path}')
println('Parts: ${parts}')
for i := 0; i < parts.len; i++ {
mut found := false
children := current.children(false)!
println('Current directory: ${current.metadata.name}')
println('Children: ${children}')
for child in children {
if child.metadata.name == parts[i] {
println('Found match: ${child.metadata.name}')
match child {
ourdb_fs.Directory {
current = child
found = true
break
}
else {
if i == parts.len - 1 {
return child
} else {
return error('Not a directory: ${parts[i]}')
}
}
}
}
}
if !found {
println('Path not found: ${parts[i]}')
return error('Path not found: ${path}')
}
}
return ourdb_fs.FSEntry(current)
}
fn (mut self OurDBVFS) get_directory(path string) !&ourdb_fs.Directory {
mut entry := self.get_entry(path)!
if mut entry is ourdb_fs.Directory {
return &entry
}
return error('Not a directory: ${path}')
}
fn convert_to_vfscore_entry(entry ourdb_fs.FSEntry) vfscore.FSEntry {
match entry {
ourdb_fs.Directory {
println('Entry is a directory: ${entry}')
println('Entry is a directory: ${convert_metadata(entry.metadata)}')
return &DirectoryEntry{
metadata: convert_metadata(entry.metadata)
path: entry.metadata.name
}
}
ourdb_fs.File {
println('Entry is a file: ${entry}')
return &FileEntry{
metadata: convert_metadata(entry.metadata)
path: entry.metadata.name
}
}
ourdb_fs.Symlink {
return &SymlinkEntry{
metadata: convert_metadata(entry.metadata)
path: entry.metadata.name
target: entry.target
}
}
}
}
fn convert_metadata(meta ourdb_fs.Metadata) vfscore.Metadata {
return vfscore.Metadata{
name: meta.name
file_type: match meta.file_type {
.file { vfscore.FileType.file }
.directory { vfscore.FileType.directory }
.symlink { vfscore.FileType.symlink }
}
size: meta.size
created_at: meta.created_at
modified_at: meta.modified_at
accessed_at: meta.accessed_at
}
}
// Entry type implementations
struct DirectoryEntry {
metadata vfscore.Metadata
path string
}
fn (e &DirectoryEntry) get_metadata() vfscore.Metadata {
return e.metadata
}
fn (e &DirectoryEntry) get_path() string {
return e.path
}
struct FileEntry {
metadata vfscore.Metadata
path string
}
fn (e &FileEntry) get_metadata() vfscore.Metadata {
return e.metadata
}
fn (e &FileEntry) get_path() string {
return e.path
}
struct SymlinkEntry {
metadata vfscore.Metadata
path string
target string
}
fn (e &SymlinkEntry) get_metadata() vfscore.Metadata {
return e.metadata
}
fn (e &SymlinkEntry) get_path() string {
return e.path
}