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

335 lines
9.2 KiB
V

module vfs_local
import os
import incubaid.herolib.vfs
// Basic operations
pub fn (myvfs LocalVFS) root_get() !vfs.FSEntry {
if !os.exists(myvfs.root_path) {
return error('Root path does not exist: ${myvfs.root_path}')
}
metadata := myvfs.os_attr_to_metadata(myvfs.root_path) or {
return error('Failed to get root metadata: ${err}')
}
return LocalFSEntry{
path: ''
metadata: metadata
}
}
// File operations with improved error handling and TOCTOU protection
pub fn (myvfs LocalVFS) file_create(path string) !vfs.FSEntry {
abs_path := myvfs.abs_path(path)
if os.exists(abs_path) {
return error('File already exists: ${path}')
}
os.write_file(abs_path, '') or { return error('Failed to create file ${path}: ${err}') }
metadata := myvfs.os_attr_to_metadata(abs_path) or {
return error('Failed to get metadata: ${err}')
}
return LocalFSEntry{
path: path
metadata: metadata
}
}
pub fn (myvfs LocalVFS) file_read(path string) ![]u8 {
abs_path := myvfs.abs_path(path)
if !os.exists(abs_path) {
return error('File does not exist: ${path}')
}
if os.is_dir(abs_path) {
return error('Path is a directory: ${path}')
}
return os.read_bytes(abs_path) or { return error('Failed to read file ${path}: ${err}') }
}
pub fn (myvfs LocalVFS) file_write(path string, data []u8) ! {
abs_path := myvfs.abs_path(path)
if os.is_dir(abs_path) {
return error('Cannot write to directory: ${path}')
}
os.write_file(abs_path, data.bytestr()) or {
return error('Failed to write file ${path}: ${err}')
}
}
pub fn (myvfs LocalVFS) file_delete(path string) ! {
abs_path := myvfs.abs_path(path)
if !os.exists(abs_path) {
return error('File does not exist: ${path}')
}
if os.is_dir(abs_path) {
return error('Cannot delete directory using file_delete: ${path}')
}
os.rm(abs_path) or { return error('Failed to delete file ${path}: ${err}') }
}
// Directory operations with improved error handling
pub fn (myvfs LocalVFS) dir_create(path string) !vfs.FSEntry {
abs_path := myvfs.abs_path(path)
if os.exists(abs_path) {
return error('Path already exists: ${path}')
}
os.mkdir_all(abs_path) or { return error('Failed to create directory ${path}: ${err}') }
metadata := myvfs.os_attr_to_metadata(abs_path) or {
return error('Failed to get metadata: ${err}')
}
return LocalFSEntry{
path: path
metadata: metadata
}
}
pub fn (myvfs LocalVFS) dir_list(path string) ![]vfs.FSEntry {
abs_path := myvfs.abs_path(path)
if !os.exists(abs_path) {
return error('Directory does not exist: ${path}')
}
if !os.is_dir(abs_path) {
return error('Path is not a directory: ${path}')
}
entries := os.ls(abs_path) or { return error('Failed to list directory ${path}: ${err}') }
mut result := []vfs.FSEntry{cap: entries.len}
for entry in entries {
rel_path := os.join_path(path, entry)
abs_entry_path := os.join_path(abs_path, entry)
metadata := myvfs.os_attr_to_metadata(abs_entry_path) or { continue } // Skip entries we can't stat
result << LocalFSEntry{
path: rel_path
metadata: metadata
}
}
return result
}
pub fn (myvfs LocalVFS) dir_delete(path string) ! {
abs_path := myvfs.abs_path(path)
if !os.exists(abs_path) {
return error('Directory does not exist: ${path}')
}
if !os.is_dir(abs_path) {
return error('Path is not a directory: ${path}')
}
os.rmdir_all(abs_path) or { return error('Failed to delete directory ${path}: ${err}') }
}
// Symlink operations with improved handling
pub fn (myvfs LocalVFS) link_create(target_path string, link_path string) !vfs.FSEntry {
abs_target := myvfs.abs_path(target_path)
abs_link := myvfs.abs_path(link_path)
if !os.exists(abs_target) {
return error('Target path does not exist: ${target_path}')
}
if os.exists(abs_link) {
return error('Link path already exists: ${link_path}')
}
os.symlink(target_path, abs_link) or {
return error('Failed to create symlink from ${target_path} to ${link_path}: ${err}')
}
metadata := myvfs.os_attr_to_metadata(abs_link) or {
return error('Failed to get metadata: ${err}')
}
return LocalFSEntry{
path: link_path
metadata: metadata
}
}
pub fn (myvfs LocalVFS) link_read(path string) !string {
abs_path := myvfs.abs_path(path)
if !os.exists(abs_path) {
return error('Symlink does not exist: ${path}')
}
if !os.is_link(abs_path) {
return error('Path is not a symlink: ${path}')
}
real_path := os.real_path(abs_path)
return os.base(real_path)
}
pub fn (myvfs LocalVFS) link_delete(path string) ! {
abs_path := myvfs.abs_path(path)
if !os.exists(abs_path) {
return error('Symlink does not exist: ${path}')
}
if !os.is_link(abs_path) {
return error('Path is not a symlink: ${path}')
}
os.rm(abs_path) or { return error('Failed to delete symlink ${path}: ${err}') }
}
// Common operations with improved error handling
pub fn (myvfs LocalVFS) exists(path string) bool {
// TODO: check is link if link the link can be broken but it stil exists
return os.exists(myvfs.abs_path(path))
}
pub fn (myvfs LocalVFS) get(path string) !vfs.FSEntry {
abs_path := myvfs.abs_path(path)
if !os.exists(abs_path) {
return error('Entry does not exist: ${path}')
}
metadata := myvfs.os_attr_to_metadata(abs_path) or {
return error('Failed to get metadata: ${err}')
}
return LocalFSEntry{
path: path
metadata: metadata
}
}
pub fn (myvfs LocalVFS) rename(old_path string, new_path string) !vfs.FSEntry {
abs_old := myvfs.abs_path(old_path)
abs_new := myvfs.abs_path(new_path)
if !os.exists(abs_old) {
return error('Source path does not exist: ${old_path}')
}
if os.exists(abs_new) {
return error('Destination path already exists: ${new_path}')
}
os.mv(abs_old, abs_new) or {
return error('Failed to rename ${old_path} to ${new_path}: ${err}')
}
metadata := myvfs.os_attr_to_metadata(abs_new) or {
return error('Failed to get metadata: ${err}')
}
return LocalFSEntry{
path: abs_new
metadata: metadata
}
}
pub fn (myvfs LocalVFS) copy(src_path string, dst_path string) !vfs.FSEntry {
abs_src := myvfs.abs_path(src_path)
abs_dst := myvfs.abs_path(dst_path)
if !os.exists(abs_src) {
return error('Source path does not exist: ${src_path}')
}
if os.exists(abs_dst) {
return error('Destination path already exists: ${dst_path}')
}
os.cp(abs_src, abs_dst) or { return error('Failed to copy ${src_path} to ${dst_path}: ${err}') }
metadata := myvfs.os_attr_to_metadata(abs_dst) or {
return error('Failed to get metadata: ${err}')
}
return LocalFSEntry{
path: dst_path
metadata: metadata
}
}
pub fn (myvfs LocalVFS) move(src_path string, dst_path string) !vfs.FSEntry {
abs_src := myvfs.abs_path(src_path)
abs_dst := myvfs.abs_path(dst_path)
if !os.exists(abs_src) {
return error('Source path does not exist: ${src_path}')
}
if os.exists(abs_dst) {
return error('Destination path already exists: ${dst_path}')
}
os.mv(abs_src, abs_dst) or { return error('Failed to move ${src_path} to ${dst_path}: ${err}') }
metadata := myvfs.os_attr_to_metadata(abs_dst) or {
return error('Failed to get metadata: ${err}')
}
return LocalFSEntry{
path: dst_path
metadata: metadata
}
}
// Generic delete operation that handles all types
pub fn (myvfs LocalVFS) delete(path string) ! {
abs_path := myvfs.abs_path(path)
if !os.exists(abs_path) {
return error('Path does not exist: ${path}')
}
if os.is_link(abs_path) {
myvfs.link_delete(path)!
} else if os.is_dir(abs_path) {
myvfs.dir_delete(path)!
} else {
myvfs.file_delete(path)!
}
}
// Destroy the vfscore by removing all its contents
pub fn (mut myvfs LocalVFS) destroy() ! {
if !os.exists(myvfs.root_path) {
return error('vfscore root path does not exist: ${myvfs.root_path}')
}
os.rmdir_all(myvfs.root_path) or {
return error('Failed to destroy vfscore at ${myvfs.root_path}: ${err}')
}
myvfs.init()!
}
// File concatenate operation - appends data to a file
pub fn (myvfs LocalVFS) file_concatenate(path string, data []u8) ! {
abs_path := myvfs.abs_path(path)
if !os.exists(abs_path) {
return error('File does not exist: ${path}')
}
if os.is_dir(abs_path) {
return error('Cannot concatenate to directory: ${path}')
}
// Read existing content
existing_content := os.read_bytes(abs_path) or {
return error('Failed to read file ${path}: ${err}')
}
// Create a new buffer with the combined content
mut new_content := []u8{cap: existing_content.len + data.len}
new_content << existing_content
new_content << data
// Write back to file
os.write_file(abs_path, new_content.bytestr()) or {
return error('Failed to write concatenated data to file ${path}: ${err}')
}
}
// Get path of an FSEntry
pub fn (myvfs LocalVFS) get_path(entry &vfs.FSEntry) !string {
// Check if the entry is a LocalFSEntry
local_entry := entry as LocalFSEntry
return local_entry.path
}
// Print information about the VFS
pub fn (myvfs LocalVFS) print() ! {
println('LocalVFS:')
println(' Root path: ${myvfs.root_path}')
// Print root directory contents
root_entries := myvfs.dir_list('') or {
println(' Error listing root directory: ${err}')
return
}
println(' Root entries: ${root_entries.len}')
for entry in root_entries {
metadata := entry.get_metadata()
entry_type := match metadata.file_type {
.file { 'FILE' }
.directory { 'DIR' }
.symlink { 'LINK' }
}
println(' ${entry_type} ${metadata.name}')
}
}