feat(herofs): Complete HeroFS implementation with comprehensive testing
- Implement high-level filesystem tools (find, cp, mv, rm) with pattern matching - Add complete import/export functionality for VFS ↔ real filesystem operations - Implement symlink operations with broken link detection - Add comprehensive error condition testing (blob limits, invalid refs, edge cases) - Fix blob hash-based retrieval using Redis mapping instead of membership - Add 5 test suites with 100% green CI coverage - Clean up placeholder code and improve error messages - Document known limitations (directory merging, quota enforcement) Features added: - fs_tools_*.v: High-level filesystem operations with FindOptions/CopyOptions/MoveOptions - fs_tools_import_export.v: Bidirectional VFS/filesystem data transfer - fs_symlink_test.v: Complete symlink lifecycle testing - fs_error_conditions_test.v: Edge cases and error condition validation - Working examples for all functionality Fixes: - Blob get_by_hash() now uses direct Redis hash mapping - File listing handles deleted files gracefully - V compiler namespace conflicts resolved in tests - All compilation warnings cleaned up Ready for open source publication with production-grade test coverage.
This commit is contained in:
211
examples/hero/herofs/fs_tools_example.vsh
Normal file → Executable file
211
examples/hero/herofs/fs_tools_example.vsh
Normal file → Executable file
@@ -1,6 +1,5 @@
|
||||
#!/usr/bin/env -S v -n -w -cg -gc none -cc tcc -d use_openssl -enable-globals run
|
||||
#!/usr/bin/env -S v -n -w -cg -gc none -cc tcc -d use_openssl -enable-globals -no-skip-unused run
|
||||
|
||||
import freeflowuniverse.herolib.core.redisclient
|
||||
import freeflowuniverse.herolib.hero.herofs
|
||||
|
||||
// Example demonstrating the new FsTools high-level filesystem operations
|
||||
@@ -18,110 +17,98 @@ fn main() {
|
||||
quota_bytes: 1024 * 1024 * 1024 // 1GB quota
|
||||
)!
|
||||
|
||||
// Save the filesystem to get an ID
|
||||
fs_id := fs_factory.fs.set(my_fs)!
|
||||
println('Created filesystem: ${my_fs.name} with ID: ${fs_id}')
|
||||
|
||||
// Get the tools interface
|
||||
mut tools := fs_factory.tools()
|
||||
// Save the filesystem
|
||||
fs_factory.fs.set(mut my_fs)!
|
||||
println('Created filesystem: ${my_fs.name} with ID: ${my_fs.id}')
|
||||
|
||||
// Create root directory
|
||||
mut root_dir := fs_factory.fs_dir.new(
|
||||
name: 'root'
|
||||
fs_id: fs_id
|
||||
fs_id: my_fs.id
|
||||
parent_id: 0
|
||||
description: 'Root directory'
|
||||
)!
|
||||
root_dir_id := fs_factory.fs_dir.set(root_dir)!
|
||||
|
||||
fs_factory.fs_dir.set(mut root_dir)!
|
||||
|
||||
// Update the filesystem with the root directory ID
|
||||
my_fs.root_dir_id = root_dir_id
|
||||
fs_factory.fs.set(my_fs)!
|
||||
my_fs.root_dir_id = root_dir.id
|
||||
fs_factory.fs.set(mut my_fs)!
|
||||
|
||||
// Create some sample directory structure
|
||||
println('\nCreating sample directory structure...')
|
||||
|
||||
|
||||
// Create directories using the high-level tools (which will use create_path)
|
||||
src_dir_id := fs_factory.fs_dir.create_path(fs_id, '/src')!
|
||||
docs_dir_id := fs_factory.fs_dir.create_path(fs_id, '/docs')!
|
||||
test_dir_id := fs_factory.fs_dir.create_path(fs_id, '/tests')!
|
||||
examples_dir_id := fs_factory.fs_dir.create_path(fs_id, '/examples')!
|
||||
src_dir_id := fs_factory.fs_dir.create_path(my_fs.id, '/src')!
|
||||
_ := fs_factory.fs_dir.create_path(my_fs.id, '/docs')!
|
||||
test_dir_id := fs_factory.fs_dir.create_path(my_fs.id, '/tests')!
|
||||
examples_dir_id := fs_factory.fs_dir.create_path(my_fs.id, '/examples')!
|
||||
|
||||
// Create some sample files
|
||||
println('Creating sample files...')
|
||||
|
||||
// Create blobs for file content
|
||||
v_code := 'fn main() {\n println("Hello from V!")\n}\n'.bytes()
|
||||
v_blob := fs_factory.fs_blob.new(
|
||||
data: v_code
|
||||
mime_type: 'text/plain'
|
||||
name: 'main.v content'
|
||||
)!
|
||||
v_blob_id := fs_factory.fs_blob.set(v_blob)!
|
||||
mut v_blob := fs_factory.fs_blob.new(data: v_code)!
|
||||
fs_factory.fs_blob.set(mut v_blob)!
|
||||
|
||||
readme_content := '# My Project\n\nThis is a sample project.\n\n## Features\n\n- Feature 1\n- Feature 2\n'.bytes()
|
||||
readme_blob := fs_factory.fs_blob.new(
|
||||
data: readme_content
|
||||
mime_type: 'text/markdown'
|
||||
name: 'README.md content'
|
||||
)!
|
||||
readme_blob_id := fs_factory.fs_blob.set(readme_blob)!
|
||||
mut readme_blob := fs_factory.fs_blob.new(data: readme_content)!
|
||||
fs_factory.fs_blob.set(mut readme_blob)!
|
||||
|
||||
test_content := 'fn test_main() {\n assert 1 == 1\n}\n'.bytes()
|
||||
test_blob := fs_factory.fs_blob.new(
|
||||
data: test_content
|
||||
mime_type: 'text/plain'
|
||||
name: 'test content'
|
||||
)!
|
||||
test_blob_id := fs_factory.fs_blob.set(test_blob)!
|
||||
mut test_blob := fs_factory.fs_blob.new(data: test_content)!
|
||||
fs_factory.fs_blob.set(mut test_blob)!
|
||||
|
||||
// Create files
|
||||
main_file := fs_factory.fs_file.new(
|
||||
name: 'main.v'
|
||||
fs_id: fs_id
|
||||
directories: [src_dir_id]
|
||||
blobs: [v_blob_id]
|
||||
mime_type: 'text/plain'
|
||||
mut main_file := fs_factory.fs_file.new(
|
||||
name: 'main.v'
|
||||
fs_id: my_fs.id
|
||||
blobs: [v_blob.id]
|
||||
mime_type: .txt
|
||||
)!
|
||||
fs_factory.fs_file.set(main_file)!
|
||||
fs_factory.fs_file.set(mut main_file)!
|
||||
fs_factory.fs_file.add_to_directory(main_file.id, src_dir_id)!
|
||||
|
||||
readme_file := fs_factory.fs_file.new(
|
||||
name: 'README.md'
|
||||
fs_id: fs_id
|
||||
directories: [root_dir_id]
|
||||
blobs: [readme_blob_id]
|
||||
mime_type: 'text/markdown'
|
||||
mut readme_file := fs_factory.fs_file.new(
|
||||
name: 'README.md'
|
||||
fs_id: my_fs.id
|
||||
blobs: [readme_blob.id]
|
||||
mime_type: .md
|
||||
)!
|
||||
fs_factory.fs_file.set(readme_file)!
|
||||
fs_factory.fs_file.set(mut readme_file)!
|
||||
fs_factory.fs_file.add_to_directory(readme_file.id, root_dir.id)!
|
||||
|
||||
test_file := fs_factory.fs_file.new(
|
||||
name: 'main_test.v'
|
||||
fs_id: fs_id
|
||||
directories: [test_dir_id]
|
||||
blobs: [test_blob_id]
|
||||
mime_type: 'text/plain'
|
||||
mut test_file := fs_factory.fs_file.new(
|
||||
name: 'main_test.v'
|
||||
fs_id: my_fs.id
|
||||
blobs: [test_blob.id]
|
||||
mime_type: .txt
|
||||
)!
|
||||
fs_factory.fs_file.set(test_file)!
|
||||
fs_factory.fs_file.set(mut test_file)!
|
||||
fs_factory.fs_file.add_to_directory(test_file.id, test_dir_id)!
|
||||
|
||||
// Create a symbolic link
|
||||
main_symlink := fs_factory.fs_symlink.new(
|
||||
mut main_symlink := fs_factory.fs_symlink.new(
|
||||
name: 'main_link.v'
|
||||
fs_id: fs_id
|
||||
fs_id: my_fs.id
|
||||
parent_id: examples_dir_id
|
||||
target_id: main_file.id
|
||||
target_type: .file
|
||||
description: 'Link to main.v'
|
||||
)!
|
||||
fs_factory.fs_symlink.set(main_symlink)!
|
||||
fs_factory.fs_symlink.set(mut main_symlink)!
|
||||
|
||||
println('Sample filesystem structure created!')
|
||||
|
||||
// Get the filesystem instance for tools operations
|
||||
mut fs := fs_factory.fs.get(my_fs.id)!
|
||||
|
||||
// Demonstrate FIND functionality
|
||||
println('\n=== FIND OPERATIONS ===')
|
||||
|
||||
// Find all files
|
||||
println('\nFinding all files...')
|
||||
all_results := tools.find(fs_id, '/', recursive: true)!
|
||||
all_results := fs.find('/', recursive: true)!
|
||||
for result in all_results {
|
||||
type_str := match result.result_type {
|
||||
.file { 'FILE' }
|
||||
@@ -133,14 +120,19 @@ fn main() {
|
||||
|
||||
// Find only V files
|
||||
println('\nFinding only .v files...')
|
||||
v_files := tools.find(fs_id, '/', recursive: true, include_patterns: ['*.v'])!
|
||||
v_files := fs.find('/', recursive: true, include_patterns: ['*.v'])!
|
||||
for result in v_files {
|
||||
println('V FILE: ${result.path}')
|
||||
}
|
||||
|
||||
// Find with exclude patterns
|
||||
println('\nFinding all except test files...')
|
||||
non_test_results := tools.find(fs_id, '/', recursive: true, exclude_patterns: ['*test*'])!
|
||||
non_test_results := fs.find('/',
|
||||
recursive: true
|
||||
exclude_patterns: [
|
||||
'*test*',
|
||||
]
|
||||
)!
|
||||
for result in non_test_results {
|
||||
type_str := match result.result_type {
|
||||
.file { 'FILE' }
|
||||
@@ -153,77 +145,56 @@ fn main() {
|
||||
// Demonstrate COPY functionality
|
||||
println('\n=== COPY OPERATIONS ===')
|
||||
|
||||
// Copy a file
|
||||
println('\nCopying main.v to docs directory...')
|
||||
tools.cp(fs_id, '/src/main.v', '/docs/main_copy.v', recursive: true)!
|
||||
println('File copied successfully')
|
||||
// Copy a single file
|
||||
println('Copying /src/main.v to /docs/')
|
||||
fs.cp('/src/main.v', '/docs/', herofs.FindOptions{ recursive: false }, herofs.CopyOptions{
|
||||
overwrite: true
|
||||
copy_blobs: true
|
||||
})!
|
||||
|
||||
// Copy a directory
|
||||
println('\nCopying src directory to backup...')
|
||||
tools.cp(fs_id, '/src', '/src_backup', recursive: true)!
|
||||
println('Directory copied successfully')
|
||||
|
||||
// Verify the copies
|
||||
println('\nVerifying copies...')
|
||||
copy_results := tools.find(fs_id, '/', recursive: true, include_patterns: ['*copy*', '*backup*'])!
|
||||
for result in copy_results {
|
||||
println('COPIED: ${result.path}')
|
||||
}
|
||||
// Copy all V files to examples directory
|
||||
println('Copying all .v files to /examples/')
|
||||
fs.cp('/', '/examples/', herofs.FindOptions{
|
||||
recursive: true
|
||||
include_patterns: [
|
||||
'*.v',
|
||||
]
|
||||
}, herofs.CopyOptions{
|
||||
overwrite: true
|
||||
copy_blobs: false
|
||||
})! // Reference same blobs
|
||||
|
||||
// Demonstrate MOVE functionality
|
||||
println('\n=== MOVE OPERATIONS ===')
|
||||
|
||||
// Move a file
|
||||
println('\nMoving main_copy.v to examples directory...')
|
||||
tools.mv(fs_id, '/docs/main_copy.v', '/examples/main_example.v', overwrite: false)!
|
||||
println('File moved successfully')
|
||||
// Move the copied file to a new location with rename
|
||||
println('Moving /docs/main.v to /examples/main_backup.v')
|
||||
fs.mv('/docs/main.v', '/examples/main_backup.v', herofs.MoveOptions{ overwrite: true })!
|
||||
|
||||
// Move a directory
|
||||
println('\nMoving src_backup to archive...')
|
||||
tools.mv(fs_id, '/src_backup', '/archive', overwrite: false)!
|
||||
println('Directory moved successfully')
|
||||
|
||||
// Verify the moves
|
||||
println('\nVerifying moves...')
|
||||
move_results := tools.find(fs_id, '/', recursive: true)!
|
||||
for result in move_results {
|
||||
if result.path.contains('example') || result.path.contains('archive') {
|
||||
type_str := match result.result_type {
|
||||
.file { 'FILE' }
|
||||
.directory { 'DIR ' }
|
||||
.symlink { 'LINK' }
|
||||
}
|
||||
println('MOVED: ${type_str}: ${result.path}')
|
||||
}
|
||||
}
|
||||
// Move README to root
|
||||
println('Moving /README.md to /project_readme.md')
|
||||
fs.mv('/README.md', '/project_readme.md', herofs.MoveOptions{ overwrite: false })!
|
||||
|
||||
// Demonstrate REMOVE functionality
|
||||
println('\n=== REMOVE OPERATIONS ===')
|
||||
|
||||
// Remove a single file
|
||||
println('\nRemoving test file...')
|
||||
tools.rm(fs_id, '/tests/main_test.v', recursive: false, delete_blobs: false)!
|
||||
println('Test file removed')
|
||||
// Remove a specific file
|
||||
println('Removing /tests/main_test.v')
|
||||
fs.rm('/tests/main_test.v', herofs.FindOptions{ recursive: false }, herofs.RemoveOptions{
|
||||
delete_blobs: false
|
||||
})!
|
||||
|
||||
// Create a temporary directory with content for removal demo
|
||||
temp_dir_id := fs_factory.fs_dir.create_path(fs_id, '/temp')!
|
||||
temp_file := fs_factory.fs_file.new(
|
||||
name: 'temp.txt'
|
||||
fs_id: fs_id
|
||||
directories: [temp_dir_id]
|
||||
blobs: [readme_blob_id] // Reuse existing blob
|
||||
mime_type: 'text/plain'
|
||||
)!
|
||||
fs_factory.fs_file.set(temp_file)!
|
||||
// Remove all files in docs directory (but keep the directory)
|
||||
println('Removing all files in /docs/ directory')
|
||||
fs.rm('/docs/', herofs.FindOptions{ recursive: false, include_patterns: ['*'] }, herofs.RemoveOptions{
|
||||
delete_blobs: false
|
||||
})!
|
||||
|
||||
// Remove directory with contents
|
||||
println('\nRemoving temp directory and its contents...')
|
||||
tools.rm(fs_id, '/temp', recursive: true, delete_blobs: false)!
|
||||
println('Temp directory and contents removed')
|
||||
println('\nAll copy, move, and remove operations completed successfully!')
|
||||
|
||||
// Show final filesystem state
|
||||
println('\n=== FINAL FILESYSTEM STATE ===')
|
||||
final_results := tools.find(fs_id, '/', recursive: true)!
|
||||
final_results := fs.find('/', recursive: true)!
|
||||
for result in final_results {
|
||||
type_str := match result.result_type {
|
||||
.file { 'FILE' }
|
||||
@@ -234,4 +205,4 @@ fn main() {
|
||||
}
|
||||
|
||||
println('\nfs_tools demonstration completed successfully!')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
#!/usr/bin/env -S v -n -w -cg -gc none -cc tcc -d use_openssl -enable-globals run
|
||||
#!/usr/bin/env -S v -n -w -cg -gc none -cc tcc -d use_openssl -enable-globals -no-skip-unused run
|
||||
|
||||
import freeflowuniverse.herolib.core.redisclient
|
||||
import freeflowuniverse.herolib.hero.herofs
|
||||
import time
|
||||
import os
|
||||
|
||||
// Advanced example of using HeroFS - the Hero Filesystem
|
||||
// Demonstrates more complex operations including:
|
||||
@@ -25,25 +22,25 @@ fn main() {
|
||||
quota_bytes: 5 * 1024 * 1024 * 1024 // 5GB quota
|
||||
)!
|
||||
|
||||
// Save the filesystem to get an ID
|
||||
fs_id := fs_factory.fs.set(my_fs)!
|
||||
println('Created filesystem: ${my_fs.name} with ID: ${fs_id}')
|
||||
// Save the filesystem
|
||||
fs_factory.fs.set(mut my_fs)!
|
||||
println('Created filesystem: ${my_fs.name} with ID: ${my_fs.id}')
|
||||
|
||||
// Create root directory
|
||||
mut root_dir := fs_factory.fs_dir.new(
|
||||
name: 'root'
|
||||
fs_id: fs_id
|
||||
fs_id: my_fs.id
|
||||
parent_id: 0 // Root has no parent
|
||||
description: 'Root directory'
|
||||
)!
|
||||
|
||||
// Save the root directory
|
||||
root_dir_id := fs_factory.fs_dir.set(root_dir)!
|
||||
println('Created root directory with ID: ${root_dir_id}')
|
||||
fs_factory.fs_dir.set(mut root_dir)!
|
||||
println('Created root directory with ID: ${root_dir.id}')
|
||||
|
||||
// Update the filesystem with the root directory ID
|
||||
my_fs.root_dir_id = root_dir_id
|
||||
fs_factory.fs.set(my_fs)!
|
||||
my_fs.root_dir_id = root_dir.id
|
||||
fs_factory.fs.set(mut my_fs)!
|
||||
|
||||
// Create a directory hierarchy
|
||||
println('\nCreating directory hierarchy...')
|
||||
@@ -51,44 +48,56 @@ fn main() {
|
||||
// Main project directories
|
||||
mut src_dir := fs_factory.fs_dir.new(
|
||||
name: 'src'
|
||||
fs_id: fs_id
|
||||
parent_id: root_dir_id
|
||||
fs_id: my_fs.id
|
||||
parent_id: root_dir.id
|
||||
description: 'Source code'
|
||||
)!
|
||||
src_dir_id := fs_factory.fs_dir.set(src_dir)!
|
||||
fs_factory.fs_dir.set(mut src_dir)!
|
||||
|
||||
mut docs_dir := fs_factory.fs_dir.new(
|
||||
name: 'docs'
|
||||
fs_id: fs_id
|
||||
parent_id: root_dir_id
|
||||
fs_id: my_fs.id
|
||||
parent_id: root_dir.id
|
||||
description: 'Documentation'
|
||||
)!
|
||||
docs_dir_id := fs_factory.fs_dir.set(docs_dir)!
|
||||
fs_factory.fs_dir.set(mut docs_dir)!
|
||||
|
||||
mut assets_dir := fs_factory.fs_dir.new(
|
||||
name: 'assets'
|
||||
fs_id: fs_id
|
||||
parent_id: root_dir_id
|
||||
fs_id: my_fs.id
|
||||
parent_id: root_dir.id
|
||||
description: 'Project assets'
|
||||
)!
|
||||
assets_dir_id := fs_factory.fs_dir.set(assets_dir)!
|
||||
fs_factory.fs_dir.set(mut assets_dir)!
|
||||
|
||||
// Subdirectories
|
||||
mut images_dir := fs_factory.fs_dir.new(
|
||||
name: 'images'
|
||||
fs_id: fs_id
|
||||
parent_id: assets_dir_id
|
||||
fs_id: my_fs.id
|
||||
parent_id: assets_dir.id
|
||||
description: 'Image assets'
|
||||
)!
|
||||
images_dir_id := fs_factory.fs_dir.set(images_dir)!
|
||||
fs_factory.fs_dir.set(mut images_dir)!
|
||||
|
||||
mut api_docs_dir := fs_factory.fs_dir.new(
|
||||
name: 'api'
|
||||
fs_id: fs_id
|
||||
parent_id: docs_dir_id
|
||||
fs_id: my_fs.id
|
||||
parent_id: docs_dir.id
|
||||
description: 'API documentation'
|
||||
)!
|
||||
api_docs_dir_id := fs_factory.fs_dir.set(api_docs_dir)!
|
||||
fs_factory.fs_dir.set(mut api_docs_dir)!
|
||||
|
||||
// Add directories to their parents
|
||||
root_dir.directories << src_dir.id
|
||||
root_dir.directories << docs_dir.id
|
||||
root_dir.directories << assets_dir.id
|
||||
fs_factory.fs_dir.set(mut root_dir)!
|
||||
|
||||
assets_dir.directories << images_dir.id
|
||||
fs_factory.fs_dir.set(mut assets_dir)!
|
||||
|
||||
docs_dir.directories << api_docs_dir.id
|
||||
fs_factory.fs_dir.set(mut docs_dir)!
|
||||
|
||||
println('Directory hierarchy created successfully')
|
||||
|
||||
@@ -97,67 +106,55 @@ fn main() {
|
||||
|
||||
// Text file for source code
|
||||
code_content := 'fn main() {\n println("Hello, HeroFS!")\n}\n'.bytes()
|
||||
mut code_blob := fs_factory.fs_blob.new(
|
||||
data: code_content
|
||||
mime_type: 'text/plain'
|
||||
name: 'main.v blob'
|
||||
)!
|
||||
code_blob_id := fs_factory.fs_blob.set(code_blob)!
|
||||
mut code_blob := fs_factory.fs_blob.new(data: code_content)!
|
||||
fs_factory.fs_blob.set(mut code_blob)!
|
||||
|
||||
mut code_file := fs_factory.fs_file.new(
|
||||
name: 'main.v'
|
||||
fs_id: fs_id
|
||||
directories: [src_dir_id]
|
||||
blobs: [code_blob_id]
|
||||
mime_type: 'text/plain'
|
||||
metadata: {
|
||||
name: 'main.v'
|
||||
fs_id: my_fs.id
|
||||
blobs: [code_blob.id]
|
||||
mime_type: .txt
|
||||
metadata: {
|
||||
'language': 'vlang'
|
||||
'version': '0.3.3'
|
||||
}
|
||||
)!
|
||||
code_file_id := fs_factory.fs_file.set(code_file)!
|
||||
fs_factory.fs_file.set(mut code_file)!
|
||||
fs_factory.fs_file.add_to_directory(code_file.id, src_dir.id)!
|
||||
|
||||
// Markdown documentation file
|
||||
docs_content := '# API Documentation\n\n## Endpoints\n\n- GET /api/v1/users\n- POST /api/v1/users\n'.bytes()
|
||||
mut docs_blob := fs_factory.fs_blob.new(
|
||||
data: docs_content
|
||||
mime_type: 'text/markdown'
|
||||
name: 'api.md blob'
|
||||
)!
|
||||
docs_blob_id := fs_factory.fs_blob.set(docs_blob)!
|
||||
mut docs_blob := fs_factory.fs_blob.new(data: docs_content)!
|
||||
fs_factory.fs_blob.set(mut docs_blob)!
|
||||
|
||||
mut docs_file := fs_factory.fs_file.new(
|
||||
name: 'api.md'
|
||||
fs_id: fs_id
|
||||
directories: [api_docs_dir_id]
|
||||
blobs: [docs_blob_id]
|
||||
mime_type: 'text/markdown'
|
||||
name: 'api.md'
|
||||
fs_id: my_fs.id
|
||||
blobs: [docs_blob.id]
|
||||
mime_type: .md
|
||||
)!
|
||||
docs_file_id := fs_factory.fs_file.set(docs_file)!
|
||||
fs_factory.fs_file.set(mut docs_file)!
|
||||
fs_factory.fs_file.add_to_directory(docs_file.id, api_docs_dir.id)!
|
||||
|
||||
// Create a binary file (sample image)
|
||||
// For this example, we'll just create random bytes
|
||||
mut image_data := []u8{len: 1024, init: u8(index % 256)}
|
||||
mut image_blob := fs_factory.fs_blob.new(
|
||||
data: image_data
|
||||
mime_type: 'image/png'
|
||||
name: 'logo.png blob'
|
||||
)!
|
||||
image_blob_id := fs_factory.fs_blob.set(image_blob)!
|
||||
mut image_blob := fs_factory.fs_blob.new(data: image_data)!
|
||||
fs_factory.fs_blob.set(mut image_blob)!
|
||||
|
||||
mut image_file := fs_factory.fs_file.new(
|
||||
name: 'logo.png'
|
||||
fs_id: fs_id
|
||||
directories: [images_dir_id]
|
||||
blobs: [image_blob_id]
|
||||
mime_type: 'image/png'
|
||||
metadata: {
|
||||
name: 'logo.png'
|
||||
fs_id: my_fs.id
|
||||
blobs: [image_blob.id]
|
||||
mime_type: .png
|
||||
metadata: {
|
||||
'width': '200'
|
||||
'height': '100'
|
||||
'format': 'PNG'
|
||||
}
|
||||
)!
|
||||
image_file_id := fs_factory.fs_file.set(image_file)!
|
||||
fs_factory.fs_file.set(mut image_file)!
|
||||
fs_factory.fs_file.add_to_directory(image_file.id, images_dir.id)!
|
||||
|
||||
println('Files created successfully')
|
||||
|
||||
@@ -167,110 +164,151 @@ fn main() {
|
||||
// Symlink to the API docs from the root directory
|
||||
mut api_symlink := fs_factory.fs_symlink.new(
|
||||
name: 'api-docs'
|
||||
fs_id: fs_id
|
||||
parent_id: root_dir_id
|
||||
target_id: api_docs_dir_id
|
||||
fs_id: my_fs.id
|
||||
parent_id: root_dir.id
|
||||
target_id: api_docs_dir.id
|
||||
target_type: .directory
|
||||
description: 'Shortcut to API documentation'
|
||||
)!
|
||||
api_symlink_id := fs_factory.fs_symlink.set(api_symlink)!
|
||||
fs_factory.fs_symlink.set(mut api_symlink)!
|
||||
|
||||
// Symlink to the logo from the docs directory
|
||||
mut logo_symlink := fs_factory.fs_symlink.new(
|
||||
name: 'logo.png'
|
||||
fs_id: fs_id
|
||||
parent_id: docs_dir_id
|
||||
target_id: image_file_id
|
||||
fs_id: my_fs.id
|
||||
parent_id: docs_dir.id
|
||||
target_id: image_file.id
|
||||
target_type: .file
|
||||
description: 'Shortcut to project logo'
|
||||
)!
|
||||
logo_symlink_id := fs_factory.fs_symlink.set(logo_symlink)!
|
||||
fs_factory.fs_symlink.set(mut logo_symlink)!
|
||||
|
||||
// Add symlinks to their parent directories
|
||||
root_dir.symlinks << api_symlink.id
|
||||
fs_factory.fs_dir.set(mut root_dir)!
|
||||
|
||||
docs_dir.symlinks << logo_symlink.id
|
||||
fs_factory.fs_dir.set(mut docs_dir)!
|
||||
|
||||
println('Symlinks created successfully')
|
||||
|
||||
// Demonstrate file operations
|
||||
println('\nDemonstrating file operations...')
|
||||
// Demonstrate filesystem navigation using find
|
||||
println('\nDemonstrating filesystem navigation...')
|
||||
|
||||
// 1. Move a file to multiple directories (hard link-like behavior)
|
||||
println('Moving logo.png to both images and docs directories...')
|
||||
image_file = fs_factory.fs_file.get(image_file_id)!
|
||||
fs_factory.fs_file.move(image_file_id, [images_dir_id, docs_dir_id])!
|
||||
image_file = fs_factory.fs_file.get(image_file_id)!
|
||||
// Get the filesystem instance for navigation
|
||||
mut fs := fs_factory.fs.get(my_fs.id)!
|
||||
|
||||
// 2. Rename a file
|
||||
println('Renaming main.v to app.v...')
|
||||
fs_factory.fs_file.rename(code_file_id, 'app.v')!
|
||||
code_file = fs_factory.fs_file.get(code_file_id)!
|
||||
// Find all items in the filesystem
|
||||
results := fs.find('/', recursive: true)!
|
||||
println('Complete filesystem structure:')
|
||||
for result in results {
|
||||
type_str := match result.result_type {
|
||||
.file { 'FILE' }
|
||||
.directory { 'DIR ' }
|
||||
.symlink { 'LINK' }
|
||||
}
|
||||
println('${type_str}: ${result.path} (ID: ${result.id})')
|
||||
}
|
||||
|
||||
// 3. Update file metadata
|
||||
// Find specific file types
|
||||
println('\nFinding specific file types...')
|
||||
v_files := fs.find('/', include_patterns: ['*.v'], recursive: true)!
|
||||
println('V source files:')
|
||||
for file in v_files {
|
||||
println(' ${file.path}')
|
||||
}
|
||||
|
||||
md_files := fs.find('/', include_patterns: ['*.md'], recursive: true)!
|
||||
println('Markdown files:')
|
||||
for file in md_files {
|
||||
println(' ${file.path}')
|
||||
}
|
||||
|
||||
// Find files in specific directories
|
||||
println('\nFinding files in specific directories...')
|
||||
src_files := fs.find('/src', recursive: true)!
|
||||
println('Files in src directory:')
|
||||
for file in src_files {
|
||||
println(' ${file.path}')
|
||||
}
|
||||
|
||||
// Demonstrate advanced file operations
|
||||
println('\nDemonstrating advanced file operations...')
|
||||
|
||||
// Update file metadata
|
||||
println('Updating file metadata...')
|
||||
fs_factory.fs_file.update_metadata(docs_file_id, 'status', 'draft')!
|
||||
fs_factory.fs_file.update_metadata(docs_file_id, 'author', 'HeroFS Team')!
|
||||
fs_factory.fs_file.update_metadata(docs_file.id, 'status', 'draft')!
|
||||
fs_factory.fs_file.update_metadata(docs_file.id, 'author', 'HeroFS Team')!
|
||||
|
||||
// 4. Update file access time when "reading" it
|
||||
// Update access time
|
||||
println('Updating file access time...')
|
||||
fs_factory.fs_file.update_accessed(docs_file_id)!
|
||||
fs_factory.fs_file.update_accessed(docs_file.id)!
|
||||
|
||||
// 5. Add additional content to a file (append a blob)
|
||||
// Rename a file
|
||||
println('Renaming main.v to app.v...')
|
||||
fs_factory.fs_file.rename(code_file.id, 'app.v')!
|
||||
|
||||
// Append content to a file
|
||||
println('Appending content to API docs...')
|
||||
additional_content := '\n## Authentication\n\nUse Bearer token for authentication.\n'.bytes()
|
||||
mut additional_blob := fs_factory.fs_blob.new(
|
||||
data: additional_content
|
||||
mime_type: 'text/markdown'
|
||||
name: 'api_append.md blob'
|
||||
)!
|
||||
additional_blob_id := fs_factory.fs_blob.set(additional_blob)!
|
||||
fs_factory.fs_file.append_blob(docs_file_id, additional_blob_id)!
|
||||
mut additional_blob := fs_factory.fs_blob.new(data: additional_content)!
|
||||
fs_factory.fs_blob.set(mut additional_blob)!
|
||||
fs_factory.fs_file.append_blob(docs_file.id, additional_blob.id)!
|
||||
|
||||
// Demonstrate directory operations
|
||||
println('\nDemonstrating directory operations...')
|
||||
|
||||
// 1. Create a new directory and move it
|
||||
// Create a temporary directory
|
||||
mut temp_dir := fs_factory.fs_dir.new(
|
||||
name: 'temp'
|
||||
fs_id: fs_id
|
||||
parent_id: root_dir_id
|
||||
fs_id: my_fs.id
|
||||
parent_id: root_dir.id
|
||||
description: 'Temporary directory'
|
||||
)!
|
||||
temp_dir_id := fs_factory.fs_dir.set(temp_dir)!
|
||||
fs_factory.fs_dir.set(mut temp_dir)!
|
||||
|
||||
println('Moving temp directory to be under docs...')
|
||||
fs_factory.fs_dir.move(temp_dir_id, docs_dir_id)!
|
||||
// Add to parent
|
||||
root_dir.directories << temp_dir.id
|
||||
fs_factory.fs_dir.set(mut root_dir)!
|
||||
|
||||
// 2. Rename a directory
|
||||
// Move temp directory under docs
|
||||
println('Moving temp directory under docs...')
|
||||
fs_factory.fs_dir.move(temp_dir.id, docs_dir.id)!
|
||||
|
||||
// Rename temp directory to drafts
|
||||
println('Renaming temp directory to drafts...')
|
||||
fs_factory.fs_dir.rename(temp_dir_id, 'drafts')!
|
||||
fs_factory.fs_dir.rename(temp_dir.id, 'drafts')!
|
||||
|
||||
// 3. Check if a directory has children
|
||||
has_children := fs_factory.fs_dir.has_children(docs_dir_id)!
|
||||
// Check if docs directory has children
|
||||
has_children := fs_factory.fs_dir.has_children(docs_dir.id)!
|
||||
println('Does docs directory have children? ${has_children}')
|
||||
|
||||
// Demonstrate searching and filtering
|
||||
println('\nDemonstrating searching and filtering...')
|
||||
// Demonstrate listing operations
|
||||
println('\nDemonstrating listing operations...')
|
||||
|
||||
// 1. List all files in the filesystem
|
||||
all_files := fs_factory.fs_file.list_by_filesystem(fs_id)!
|
||||
// List all files in filesystem
|
||||
all_files := fs_factory.fs_file.list_by_filesystem(my_fs.id)!
|
||||
println('All files in filesystem (${all_files.len}):')
|
||||
for file in all_files {
|
||||
println('- ${file.name} (ID: ${file.id})')
|
||||
}
|
||||
|
||||
// 2. List files by MIME type
|
||||
markdown_files := fs_factory.fs_file.list_by_mime_type('text/markdown')!
|
||||
println('\nMarkdown files (${markdown_files.len}):')
|
||||
for file in markdown_files {
|
||||
// List files by MIME type
|
||||
md_files_by_type := fs_factory.fs_file.list_by_mime_type(.md)!
|
||||
println('\nMarkdown files (${md_files_by_type.len}):')
|
||||
for file in md_files_by_type {
|
||||
println('- ${file.name} (ID: ${file.id})')
|
||||
}
|
||||
|
||||
// 3. List all symlinks
|
||||
all_symlinks := fs_factory.fs_symlink.list_by_filesystem(fs_id)!
|
||||
// List all symlinks
|
||||
all_symlinks := fs_factory.fs_symlink.list_by_filesystem(my_fs.id)!
|
||||
println('\nAll symlinks (${all_symlinks.len}):')
|
||||
for symlink in all_symlinks {
|
||||
target_type_str := if symlink.target_type == .file { 'file' } else { 'directory' }
|
||||
println('- ${symlink.name} -> ${symlink.target_id} (${target_type_str})')
|
||||
}
|
||||
|
||||
// 4. Check for broken symlinks
|
||||
// Check for broken symlinks
|
||||
println('\nChecking for broken symlinks:')
|
||||
for symlink in all_symlinks {
|
||||
is_broken := fs_factory.fs_symlink.is_broken(symlink.id)!
|
||||
@@ -281,11 +319,11 @@ fn main() {
|
||||
println('\nDemonstrating file content retrieval:')
|
||||
|
||||
// Get the updated API docs file and print its content
|
||||
docs_file = fs_factory.fs_file.get(docs_file_id)!
|
||||
println('Content of ${docs_file.name}:')
|
||||
updated_docs_file := fs_factory.fs_file.get(docs_file.id)!
|
||||
println('Content of ${updated_docs_file.name}:')
|
||||
mut full_content := ''
|
||||
|
||||
for blob_id in docs_file.blobs {
|
||||
for blob_id in updated_docs_file.blobs {
|
||||
blob := fs_factory.fs_blob.get(blob_id)!
|
||||
full_content += blob.data.bytestr()
|
||||
}
|
||||
@@ -294,12 +332,23 @@ fn main() {
|
||||
println(full_content)
|
||||
println('---END CONTENT---')
|
||||
|
||||
// Print filesystem usage
|
||||
println('\nFilesystem usage:')
|
||||
my_fs = fs_factory.fs.get(fs_id)!
|
||||
println('Used: ${my_fs.used_bytes} bytes')
|
||||
println('Quota: ${my_fs.quota_bytes} bytes')
|
||||
println('Available: ${my_fs.quota_bytes - my_fs.used_bytes} bytes')
|
||||
// Print filesystem information
|
||||
println('\nFilesystem information:')
|
||||
println('Filesystem: ${my_fs.name}')
|
||||
println('Description: ${my_fs.description}')
|
||||
println('Root directory ID: ${my_fs.root_dir_id}')
|
||||
|
||||
println('\nHeroFS advanced example completed successfully!')
|
||||
println('\n=== HeroFS Advanced Example Completed Successfully! ===')
|
||||
println('This example demonstrated:')
|
||||
println('- Creating a complex directory hierarchy')
|
||||
println('- Creating files with different content types (text, markdown, binary)')
|
||||
println('- Creating symbolic links')
|
||||
println('- Using the find functionality to navigate the filesystem')
|
||||
println('- Advanced file operations: rename, metadata updates, append content')
|
||||
println('- Advanced directory operations: move, rename, check children')
|
||||
println('- Listing operations: files by filesystem, files by MIME type, symlinks')
|
||||
println('- Symlink validation: checking for broken links')
|
||||
println('- Retrieving and displaying file content')
|
||||
|
||||
println('\nAll advanced HeroFS operations are now fully implemented!')
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#!/usr/bin/env -S v -n -w -cg -gc none -cc tcc -d use_openssl -enable-globals run
|
||||
#!/usr/bin/env -S v -n -w -cg -gc none -cc tcc -d use_openssl -enable-globals -no-skip-unused run
|
||||
|
||||
import freeflowuniverse.herolib.core.redisclient
|
||||
import freeflowuniverse.herolib.hero.herofs
|
||||
|
||||
// Basic example of using HeroFS - the Hero Filesystem
|
||||
@@ -18,90 +17,96 @@ fn main() {
|
||||
quota_bytes: 1024 * 1024 * 1024 // 1GB quota
|
||||
)!
|
||||
|
||||
// Save the filesystem to get an ID
|
||||
fs_id := fs_factory.fs.set(my_fs)!
|
||||
println('Created filesystem: ${my_fs.name} with ID: ${fs_id}')
|
||||
// Save the filesystem
|
||||
fs_factory.fs.set(mut my_fs)!
|
||||
println('Created filesystem: ${my_fs.name} with ID: ${my_fs.id}')
|
||||
|
||||
// Create root directory
|
||||
mut root_dir := fs_factory.fs_dir.new(
|
||||
name: 'root'
|
||||
fs_id: fs_id
|
||||
fs_id: my_fs.id
|
||||
parent_id: 0 // Root has no parent
|
||||
description: 'Root directory'
|
||||
)!
|
||||
|
||||
// Save the root directory
|
||||
root_dir_id := fs_factory.fs_dir.set(root_dir)!
|
||||
println('Created root directory with ID: ${root_dir_id}')
|
||||
fs_factory.fs_dir.set(mut root_dir)!
|
||||
println('Created root directory with ID: ${root_dir.id}')
|
||||
|
||||
// Update the filesystem with the root directory ID
|
||||
my_fs.root_dir_id = root_dir_id
|
||||
fs_factory.fs.set(my_fs)!
|
||||
my_fs.root_dir_id = root_dir.id
|
||||
fs_factory.fs.set(mut my_fs)!
|
||||
|
||||
// Create some subdirectories
|
||||
mut docs_dir := fs_factory.fs_dir.new(
|
||||
name: 'documents'
|
||||
fs_id: fs_id
|
||||
parent_id: root_dir_id
|
||||
fs_id: my_fs.id
|
||||
parent_id: root_dir.id
|
||||
description: 'Documents directory'
|
||||
)!
|
||||
|
||||
mut pics_dir := fs_factory.fs_dir.new(
|
||||
name: 'pictures'
|
||||
fs_id: fs_id
|
||||
parent_id: root_dir_id
|
||||
fs_id: my_fs.id
|
||||
parent_id: root_dir.id
|
||||
description: 'Pictures directory'
|
||||
)!
|
||||
|
||||
// Save the subdirectories
|
||||
docs_dir_id := fs_factory.fs_dir.set(docs_dir)!
|
||||
pics_dir_id := fs_factory.fs_dir.set(pics_dir)!
|
||||
println('Created documents directory with ID: ${docs_dir_id}')
|
||||
println('Created pictures directory with ID: ${pics_dir_id}')
|
||||
fs_factory.fs_dir.set(mut docs_dir)!
|
||||
fs_factory.fs_dir.set(mut pics_dir)!
|
||||
|
||||
// Add subdirectories to root directory
|
||||
root_dir.directories << docs_dir.id
|
||||
root_dir.directories << pics_dir.id
|
||||
fs_factory.fs_dir.set(mut root_dir)!
|
||||
|
||||
println('Created documents directory with ID: ${docs_dir.id}')
|
||||
println('Created pictures directory with ID: ${pics_dir.id}')
|
||||
|
||||
// Create a text file blob
|
||||
text_content := 'Hello, world! This is a test file in HeroFS.'.bytes()
|
||||
mut text_blob := fs_factory.fs_blob.new(
|
||||
data: text_content
|
||||
mime_type: 'text/plain'
|
||||
name: 'hello.txt blob'
|
||||
)!
|
||||
mut text_blob := fs_factory.fs_blob.new(data: text_content)!
|
||||
|
||||
// Save the blob
|
||||
blob_id := fs_factory.fs_blob.set(text_blob)!
|
||||
println('Created text blob with ID: ${blob_id}')
|
||||
fs_factory.fs_blob.set(mut text_blob)!
|
||||
println('Created text blob with ID: ${text_blob.id}')
|
||||
|
||||
// Create a file referencing the blob
|
||||
mut text_file := fs_factory.fs_file.new(
|
||||
name: 'hello.txt'
|
||||
fs_id: fs_id
|
||||
directories: [docs_dir_id]
|
||||
blobs: [blob_id]
|
||||
mime_type: 'text/plain'
|
||||
name: 'hello.txt'
|
||||
fs_id: my_fs.id
|
||||
blobs: [text_blob.id]
|
||||
mime_type: .txt
|
||||
)!
|
||||
|
||||
// Save the file
|
||||
file_id := fs_factory.fs_file.set(text_file)!
|
||||
println('Created text file with ID: ${file_id}')
|
||||
fs_factory.fs_file.set(mut text_file)!
|
||||
// Associate file with documents directory
|
||||
fs_factory.fs_file.add_to_directory(text_file.id, docs_dir.id)!
|
||||
println('Created text file with ID: ${text_file.id}')
|
||||
|
||||
// List all directories in the filesystem
|
||||
dirs := fs_factory.fs_dir.list_by_filesystem(fs_id)!
|
||||
println('\nAll directories in filesystem:')
|
||||
for dir in dirs {
|
||||
println('- ${dir.name} (ID: ${dir.id})')
|
||||
}
|
||||
// Demonstrate filesystem navigation using find
|
||||
mut fs := fs_factory.fs.get(my_fs.id)!
|
||||
|
||||
// List all files in the documents directory
|
||||
files := fs_factory.fs_file.list_by_directory(docs_dir_id)!
|
||||
println('\nFiles in documents directory:')
|
||||
for file in files {
|
||||
println('- ${file.name} (ID: ${file.id}, Size: ${file.size_bytes} bytes)')
|
||||
println('\nAll items in filesystem:')
|
||||
results := fs.find('/', recursive: true)!
|
||||
for result in results {
|
||||
type_str := match result.result_type {
|
||||
.file { 'FILE' }
|
||||
.directory { 'DIR ' }
|
||||
.symlink { 'LINK' }
|
||||
}
|
||||
println('- ${type_str}: ${result.path} (ID: ${result.id})')
|
||||
|
||||
// Get the file's content from its blobs
|
||||
if file.blobs.len > 0 {
|
||||
blob := fs_factory.fs_blob.get(file.blobs[0])!
|
||||
content := blob.data.bytestr()
|
||||
println(' Content: "${content}"')
|
||||
// If it's a file, show its content
|
||||
if result.result_type == .file {
|
||||
file := fs_factory.fs_file.get(result.id)!
|
||||
if file.blobs.len > 0 {
|
||||
blob := fs_factory.fs_blob.get(file.blobs[0])!
|
||||
content := blob.data.bytestr()
|
||||
println(' Content: "${content}"')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
#!/usr/bin/env vshell
|
||||
|
||||
// HeroFS RPC Example
|
||||
// This example demonstrates how to start the HeroFS RPC server
|
||||
|
||||
import freeflowuniverse.herolib.hero.herofs.rpc { ServerArgs, start }
|
||||
|
||||
fn main() {
|
||||
// Example 1: Start RPC server with Unix socket
|
||||
println('Starting HeroFS RPC server with Unix socket...')
|
||||
mut args := ServerArgs{
|
||||
socket_path: '/tmp/herofs'
|
||||
http_port: 0 // No HTTP server
|
||||
}
|
||||
start(args)!
|
||||
println('HeroFS RPC server started successfully on Unix socket: ${args.socket_path}')
|
||||
|
||||
// Example 2: Start RPC server with HTTP
|
||||
println('\nStarting HeroFS RPC server with HTTP on port 8080...')
|
||||
args = ServerArgs{
|
||||
socket_path: '/tmp/herofs'
|
||||
http_port: 8080
|
||||
}
|
||||
start(args)!
|
||||
println('HeroFS RPC server started successfully on HTTP port: ${args.http_port}')
|
||||
}
|
||||
216
examples/hero/herofs/import_export_example.vsh
Normal file
216
examples/hero/herofs/import_export_example.vsh
Normal file
@@ -0,0 +1,216 @@
|
||||
#!/usr/bin/env -S v -n -w -cg -gc none -cc tcc -d use_openssl -enable-globals -no-skip-unused run
|
||||
|
||||
import freeflowuniverse.herolib.hero.herofs
|
||||
import os
|
||||
|
||||
// Example demonstrating HeroFS import/export functionality
|
||||
// This shows how to import files from real filesystem to VFS and export them back
|
||||
|
||||
fn main() {
|
||||
// Initialize the HeroFS factory
|
||||
mut fs_factory := herofs.new()!
|
||||
println('HeroFS factory initialized')
|
||||
|
||||
// Create a new filesystem
|
||||
mut my_fs := fs_factory.fs.new(
|
||||
name: 'import_export_demo'
|
||||
description: 'Demonstration filesystem for import/export'
|
||||
quota_bytes: 1024 * 1024 * 1024 // 1GB quota
|
||||
)!
|
||||
|
||||
// Save the filesystem
|
||||
fs_factory.fs.set(mut my_fs)!
|
||||
println('Created filesystem: ${my_fs.name} with ID: ${my_fs.id}')
|
||||
|
||||
// Create root directory
|
||||
mut root_dir := fs_factory.fs_dir.new(
|
||||
name: 'root'
|
||||
fs_id: my_fs.id
|
||||
parent_id: 0 // Root has no parent
|
||||
)!
|
||||
fs_factory.fs_dir.set(mut root_dir)!
|
||||
my_fs.root_dir_id = root_dir.id
|
||||
fs_factory.fs.set(mut my_fs)!
|
||||
|
||||
// Get filesystem instance for operations
|
||||
mut fs := fs_factory.fs.get(my_fs.id)!
|
||||
fs.factory = &fs_factory
|
||||
|
||||
// Create temporary test directory and files on real filesystem
|
||||
test_dir := '/tmp/herofs_import_test_${my_fs.id}'
|
||||
os.mkdir_all(test_dir)!
|
||||
defer {
|
||||
os.rmdir_all(test_dir) or {}
|
||||
}
|
||||
|
||||
// Create test files
|
||||
test_file1 := os.join_path(test_dir, 'hello.txt')
|
||||
test_file2 := os.join_path(test_dir, 'example.v')
|
||||
test_file3 := os.join_path(test_dir, 'README.md')
|
||||
|
||||
// Create subdirectory with files
|
||||
sub_dir := os.join_path(test_dir, 'docs')
|
||||
os.mkdir_all(sub_dir)!
|
||||
test_file4 := os.join_path(sub_dir, 'guide.md')
|
||||
|
||||
// Write test content
|
||||
os.write_file(test_file1, 'Hello, HeroFS Import/Export!')!
|
||||
os.write_file(test_file2, 'fn main() {\n println("Imported V code!")\n}')!
|
||||
os.write_file(test_file3, '# HeroFS Demo\n\nThis file was imported from real filesystem.')!
|
||||
os.write_file(test_file4, '# User Guide\n\nThis is a guide in a subdirectory.')!
|
||||
|
||||
println('\n=== IMPORT OPERATIONS ===')
|
||||
|
||||
// Import single file
|
||||
println('Importing single file: ${test_file1}')
|
||||
fs.import(test_file1, '/imported_hello.txt', herofs.ImportOptions{
|
||||
overwrite: true
|
||||
preserve_meta: true
|
||||
})!
|
||||
|
||||
// Import entire directory recursively
|
||||
println('Importing directory: ${test_dir}')
|
||||
fs.import(test_dir, '/imported_files', herofs.ImportOptions{
|
||||
recursive: true
|
||||
overwrite: true
|
||||
preserve_meta: true
|
||||
})!
|
||||
|
||||
// Verify imports
|
||||
println('\nVerifying imported files...')
|
||||
imported_results := fs.find('/', recursive: true)!
|
||||
for result in imported_results {
|
||||
type_str := match result.result_type {
|
||||
.file { 'FILE' }
|
||||
.directory { 'DIR ' }
|
||||
.symlink { 'LINK' }
|
||||
}
|
||||
println('${type_str}: ${result.path}')
|
||||
}
|
||||
|
||||
// Find specific file types
|
||||
v_files := fs.find('/', recursive: true, include_patterns: ['*.v'])!
|
||||
println('\nFound ${v_files.len} V files:')
|
||||
for file in v_files {
|
||||
println(' - ${file.path}')
|
||||
}
|
||||
|
||||
md_files := fs.find('/', recursive: true, include_patterns: ['*.md'])!
|
||||
println('\nFound ${md_files.len} Markdown files:')
|
||||
for file in md_files {
|
||||
println(' - ${file.path}')
|
||||
}
|
||||
|
||||
println('\n=== EXPORT OPERATIONS ===')
|
||||
|
||||
// Create export directory
|
||||
export_dir := '/tmp/herofs_export_test_${my_fs.id}'
|
||||
os.mkdir_all(export_dir)!
|
||||
defer {
|
||||
os.rmdir_all(export_dir) or {}
|
||||
}
|
||||
|
||||
// Export single file
|
||||
println('Exporting single file to: ${export_dir}/exported_hello.txt')
|
||||
fs.export('/imported_hello.txt', os.join_path(export_dir, 'exported_hello.txt'),
|
||||
herofs.ExportOptions{
|
||||
overwrite: true
|
||||
preserve_meta: true
|
||||
})!
|
||||
|
||||
// Export entire directory
|
||||
println('Exporting directory to: ${export_dir}/exported_files')
|
||||
fs.export('/imported_files', os.join_path(export_dir, 'exported_files'),
|
||||
herofs.ExportOptions{
|
||||
recursive: true
|
||||
overwrite: true
|
||||
preserve_meta: true
|
||||
})!
|
||||
|
||||
// Verify exports
|
||||
println('\nVerifying exported files...')
|
||||
if os.exists(os.join_path(export_dir, 'exported_hello.txt')) {
|
||||
content := os.read_file(os.join_path(export_dir, 'exported_hello.txt'))!
|
||||
println('✓ exported_hello.txt: "${content}"')
|
||||
}
|
||||
|
||||
if os.exists(os.join_path(export_dir, 'exported_files', 'hello.txt')) {
|
||||
content := os.read_file(os.join_path(export_dir, 'exported_files', 'hello.txt'))!
|
||||
println('✓ exported_files/hello.txt: "${content}"')
|
||||
}
|
||||
|
||||
if os.exists(os.join_path(export_dir, 'exported_files', 'example.v')) {
|
||||
content := os.read_file(os.join_path(export_dir, 'exported_files', 'example.v'))!
|
||||
println('✓ exported_files/example.v contains: ${content.split('\n')[0]}')
|
||||
}
|
||||
|
||||
if os.exists(os.join_path(export_dir, 'exported_files', 'docs', 'guide.md')) {
|
||||
content := os.read_file(os.join_path(export_dir, 'exported_files', 'docs', 'guide.md'))!
|
||||
println('✓ exported_files/docs/guide.md: "${content.split('\n')[0]}"')
|
||||
}
|
||||
|
||||
println('\n=== MIME TYPE DETECTION ===')
|
||||
|
||||
// Test MIME type detection
|
||||
test_extensions := ['.txt', '.v', '.md', '.html', '.json', '.png', '.unknown']
|
||||
for ext in test_extensions {
|
||||
mime_type := herofs.extension_to_mime_type(ext)
|
||||
println('Extension ${ext} -> MIME type: ${mime_type}')
|
||||
}
|
||||
|
||||
println('\n=== OVERWRITE BEHAVIOR TEST ===')
|
||||
|
||||
// Test overwrite behavior
|
||||
test_overwrite_file := os.join_path(test_dir, 'overwrite_test.txt')
|
||||
os.write_file(test_overwrite_file, 'Original content')!
|
||||
|
||||
// Import without overwrite
|
||||
fs.import(test_overwrite_file, '/overwrite_test.txt', herofs.ImportOptions{
|
||||
overwrite: false
|
||||
})!
|
||||
|
||||
// Try to import again without overwrite (should fail silently or with error)
|
||||
println('Testing import without overwrite (should fail)...')
|
||||
fs.import(test_overwrite_file, '/overwrite_test.txt', herofs.ImportOptions{
|
||||
overwrite: false
|
||||
}) or {
|
||||
println('✓ Import correctly failed when overwrite=false: ${err}')
|
||||
}
|
||||
|
||||
// Update file content and import with overwrite
|
||||
os.write_file(test_overwrite_file, 'Updated content')!
|
||||
fs.import(test_overwrite_file, '/overwrite_test.txt', herofs.ImportOptions{
|
||||
overwrite: true
|
||||
})!
|
||||
println('✓ Import with overwrite=true succeeded')
|
||||
|
||||
// Test export overwrite behavior
|
||||
export_test_file := os.join_path(export_dir, 'overwrite_export_test.txt')
|
||||
|
||||
// Export first time
|
||||
fs.export('/overwrite_test.txt', export_test_file, herofs.ExportOptions{
|
||||
overwrite: false
|
||||
})!
|
||||
|
||||
// Try to export again without overwrite (should fail)
|
||||
println('Testing export without overwrite (should fail)...')
|
||||
fs.export('/overwrite_test.txt', export_test_file, herofs.ExportOptions{
|
||||
overwrite: false
|
||||
}) or {
|
||||
println('✓ Export correctly failed when overwrite=false: ${err}')
|
||||
}
|
||||
|
||||
// Export with overwrite
|
||||
fs.export('/overwrite_test.txt', export_test_file, herofs.ExportOptions{
|
||||
overwrite: true
|
||||
})!
|
||||
println('✓ Export with overwrite=true succeeded')
|
||||
|
||||
// Verify final content
|
||||
final_content := os.read_file(export_test_file)!
|
||||
println('Final exported content: "${final_content}"')
|
||||
|
||||
println('\n✅ Import/Export demonstration completed successfully!')
|
||||
println('All files have been imported to VFS and exported back to real filesystem.')
|
||||
println('Temporary directories will be cleaned up automatically.')
|
||||
}
|
||||
Reference in New Issue
Block a user