...
This commit is contained in:
143
examples/hero/herofs/fs_dir_test.vsh
Executable file
143
examples/hero/herofs/fs_dir_test.vsh
Executable file
@@ -0,0 +1,143 @@
|
|||||||
|
#!/usr/bin/env -S v -n -w -cg -gc none -cc tcc -d use_openssl -enable-globals run
|
||||||
|
|
||||||
|
import freeflowuniverse.herolib.hero.herofs
|
||||||
|
import freeflowuniverse.herolib.hero.db
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println('Testing FsDir functionality...')
|
||||||
|
|
||||||
|
// Initialize the HeroFS factory
|
||||||
|
mut fs_factory := herofs.new()!
|
||||||
|
println('HeroFS factory initialized')
|
||||||
|
|
||||||
|
// Create a new filesystem (required for FsDir)
|
||||||
|
mut my_fs := fs_factory.fs.new(
|
||||||
|
name: 'test_filesystem'
|
||||||
|
description: 'Filesystem for testing FsDir functionality'
|
||||||
|
quota_bytes: 1024 * 1024 * 1024 // 1GB quota
|
||||||
|
)!
|
||||||
|
|
||||||
|
// Save the filesystem to get an ID
|
||||||
|
fs_id := fs_factory.fs.set(my_fs)!
|
||||||
|
println('Created test filesystem with ID: ${fs_id}')
|
||||||
|
|
||||||
|
// Create root directory
|
||||||
|
mut root_dir := fs_factory.fs_dir.new(
|
||||||
|
name: 'root'
|
||||||
|
fs_id: fs_id
|
||||||
|
parent_id: 0 // Root has no parent
|
||||||
|
description: 'Root directory for testing'
|
||||||
|
)!
|
||||||
|
|
||||||
|
// Save the root directory
|
||||||
|
root_dir_id := fs_factory.fs_dir.set(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)!
|
||||||
|
|
||||||
|
// Create test directories with various parameters
|
||||||
|
mut test_dir1 := fs_factory.fs_dir.new(
|
||||||
|
name: 'test_dir1'
|
||||||
|
fs_id: fs_id
|
||||||
|
parent_id: root_dir_id
|
||||||
|
description: 'First test directory'
|
||||||
|
)!
|
||||||
|
|
||||||
|
mut test_dir2 := fs_factory.fs_dir.new(
|
||||||
|
name: 'test_dir2'
|
||||||
|
fs_id: fs_id
|
||||||
|
parent_id: root_dir_id
|
||||||
|
description: 'Second test directory with tags'
|
||||||
|
tags: ['test', 'directory', 'example']
|
||||||
|
)!
|
||||||
|
|
||||||
|
mut test_dir3 := fs_factory.fs_dir.new(
|
||||||
|
name: 'test_dir3'
|
||||||
|
fs_id: fs_id
|
||||||
|
parent_id: root_dir_id
|
||||||
|
description: 'Third test directory with comments'
|
||||||
|
comments: [db.CommentArg{
|
||||||
|
text: 'This is a test comment'
|
||||||
|
author: 'test_user'
|
||||||
|
}]
|
||||||
|
)!
|
||||||
|
|
||||||
|
// Save the test directories
|
||||||
|
dir1_id := fs_factory.fs_dir.set(test_dir1)!
|
||||||
|
dir2_id := fs_factory.fs_dir.set(test_dir2)!
|
||||||
|
dir3_id := fs_factory.fs_dir.set(test_dir3)!
|
||||||
|
|
||||||
|
println('Created test directories:')
|
||||||
|
println('- ${test_dir1.name} with ID: ${dir1_id}')
|
||||||
|
println('- ${test_dir2.name} with ID: ${dir2_id}')
|
||||||
|
println('- ${test_dir3.name} with ID: ${dir3_id}')
|
||||||
|
|
||||||
|
// Test loading directories by ID
|
||||||
|
println('\nTesting directory loading...')
|
||||||
|
|
||||||
|
loaded_root_dir := fs_factory.fs_dir.get(root_dir_id)!
|
||||||
|
println('Loaded root directory: ${loaded_root_dir.name} (ID: ${loaded_root_dir.id})')
|
||||||
|
|
||||||
|
loaded_dir1 := fs_factory.fs_dir.get(dir1_id)!
|
||||||
|
println('Loaded test_dir1: ${loaded_dir1.name} (ID: ${loaded_dir1.id})')
|
||||||
|
println(' Description: ${loaded_dir1.description}')
|
||||||
|
|
||||||
|
loaded_dir2 := fs_factory.fs_dir.get(dir2_id)!
|
||||||
|
println('Loaded test_dir2: ${loaded_dir2.name} (ID: ${loaded_dir2.id})')
|
||||||
|
println(' Description: ${loaded_dir2.description}')
|
||||||
|
println(' Tags: ${loaded_dir2.tags}')
|
||||||
|
|
||||||
|
loaded_dir3 := fs_factory.fs_dir.get(dir3_id)!
|
||||||
|
println('Loaded test_dir3: ${loaded_dir3.name} (ID: ${loaded_dir3.id})')
|
||||||
|
println(' Description: ${loaded_dir3.description}')
|
||||||
|
|
||||||
|
// Verify that loaded directories match the original ones
|
||||||
|
println('\nVerifying data integrity...')
|
||||||
|
|
||||||
|
if loaded_root_dir.name == root_dir.name && loaded_root_dir.description == root_dir.description {
|
||||||
|
println('✓ Root directory data integrity verified')
|
||||||
|
} else {
|
||||||
|
println('✗ Root directory data integrity check failed')
|
||||||
|
}
|
||||||
|
|
||||||
|
if loaded_dir1.name == test_dir1.name && loaded_dir1.description == test_dir1.description {
|
||||||
|
println('✓ Test directory 1 data integrity verified')
|
||||||
|
} else {
|
||||||
|
println('✗ Test directory 1 data integrity check failed')
|
||||||
|
}
|
||||||
|
|
||||||
|
if loaded_dir2.name == test_dir2.name && loaded_dir2.description == test_dir2.description && loaded_dir2.tags == test_dir2.tags {
|
||||||
|
println('✓ Test directory 2 data integrity verified')
|
||||||
|
} else {
|
||||||
|
println('✗ Test directory 2 data integrity check failed')
|
||||||
|
}
|
||||||
|
|
||||||
|
if loaded_dir3.name == test_dir3.name && loaded_dir3.description == test_dir3.description {
|
||||||
|
println('✓ Test directory 3 data integrity verified')
|
||||||
|
} else {
|
||||||
|
println('✗ Test directory 3 data integrity check failed')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test exist method
|
||||||
|
println('\nTesting directory existence checks...')
|
||||||
|
|
||||||
|
exists := fs_factory.fs_dir.exist(root_dir_id)!
|
||||||
|
println('Root directory exists: ${exists}')
|
||||||
|
|
||||||
|
exists = fs_factory.fs_dir.exist(dir1_id)!
|
||||||
|
println('Test directory 1 exists: ${exists}')
|
||||||
|
|
||||||
|
exists = fs_factory.fs_dir.exist(dir2_id)!
|
||||||
|
println('Test directory 2 exists: ${exists}')
|
||||||
|
|
||||||
|
exists = fs_factory.fs_dir.exist(dir3_id)!
|
||||||
|
println('Test directory 3 exists: ${exists}')
|
||||||
|
|
||||||
|
// Test with non-existent ID
|
||||||
|
exists = fs_factory.fs_dir.exist(999999)!
|
||||||
|
println('Non-existent directory exists: ${exists}')
|
||||||
|
|
||||||
|
println('\nFsDir test completed successfully!')
|
||||||
|
}
|
||||||
@@ -5,34 +5,41 @@ import freeflowuniverse.herolib.hero.db
|
|||||||
@[heap]
|
@[heap]
|
||||||
pub struct FsFactory {
|
pub struct FsFactory {
|
||||||
pub mut:
|
pub mut:
|
||||||
fs DBFs
|
fs DBFs
|
||||||
fs_blob DBFsBlob
|
fs_blob DBFsBlob
|
||||||
fs_blob_membership DBFsBlobMembership
|
fs_blob_membership DBFsBlobMembership
|
||||||
fs_dir DBFsDir
|
fs_dir DBFsDir
|
||||||
fs_file DBFsFile
|
fs_file DBFsFile
|
||||||
fs_symlink DBFsSymlink
|
fs_symlink DBFsSymlink
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new() !FsFactory {
|
pub fn new() !FsFactory {
|
||||||
mut mydb := db.new()!
|
mut mydb := db.new()!
|
||||||
return FsFactory{
|
mut f := FsFactory{
|
||||||
fs: DBFs{
|
fs: DBFs{
|
||||||
db: &mydb
|
db: &mydb
|
||||||
}
|
}
|
||||||
fs_blob: DBFsBlob{
|
fs_blob: DBFsBlob{
|
||||||
db: &mydb
|
db: &mydb
|
||||||
}
|
}
|
||||||
fs_blob_membership: DBFsBlobMembership{
|
fs_blob_membership: DBFsBlobMembership{
|
||||||
db: &mydb
|
db: &mydb
|
||||||
}
|
}
|
||||||
fs_dir: DBFsDir{
|
fs_dir: DBFsDir{
|
||||||
db: &mydb
|
db: &mydb
|
||||||
}
|
}
|
||||||
fs_file: DBFsFile{
|
fs_file: DBFsFile{
|
||||||
db: &mydb
|
db: &mydb
|
||||||
}
|
}
|
||||||
fs_symlink: DBFsSymlink{
|
fs_symlink: DBFsSymlink{
|
||||||
db: &mydb
|
db: &mydb
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
f.fs.factory = &f
|
||||||
|
f.fs_blob.factory = &f
|
||||||
|
f.fs_blob_membership.factory = &f
|
||||||
|
f.fs_dir.factory = &f
|
||||||
|
f.fs_file.factory = &f
|
||||||
|
f.fs_symlink.factory = &f
|
||||||
|
return f
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ pub mut:
|
|||||||
|
|
||||||
pub struct DBFs {
|
pub struct DBFs {
|
||||||
pub mut:
|
pub mut:
|
||||||
db &db.DB @[skip; str: skip]
|
db &db.DB @[skip; str: skip]
|
||||||
|
factory &FsFactory = unsafe { nil } @[skip; str: skip]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (self Fs) type_name() string {
|
pub fn (self Fs) type_name() string {
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
module herofs
|
module herofs
|
||||||
|
|
||||||
import time
|
|
||||||
import crypto.blake3
|
import crypto.blake3
|
||||||
import freeflowuniverse.herolib.data.encoder
|
import freeflowuniverse.herolib.data.encoder
|
||||||
import freeflowuniverse.herolib.data.ourtime
|
import freeflowuniverse.herolib.data.ourtime
|
||||||
import freeflowuniverse.herolib.hero.db
|
import freeflowuniverse.herolib.hero.db
|
||||||
import freeflowuniverse.herolib.core.texttools
|
|
||||||
|
|
||||||
// FsBlob represents binary data up to 1MB
|
// FsBlob represents binary data up to 1MB
|
||||||
@[heap]
|
@[heap]
|
||||||
@@ -19,7 +17,8 @@ pub mut:
|
|||||||
|
|
||||||
pub struct DBFsBlob {
|
pub struct DBFsBlob {
|
||||||
pub mut:
|
pub mut:
|
||||||
db &db.DB @[skip; str: skip]
|
db &db.DB @[skip; str: skip]
|
||||||
|
factory &FsFactory = unsafe { nil } @[skip; str: skip]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (self FsBlob) type_name() string {
|
pub fn (self FsBlob) type_name() string {
|
||||||
@@ -41,7 +40,7 @@ fn (mut self DBFsBlob) load(mut o FsBlob, mut e encoder.Decoder) ! {
|
|||||||
@[params]
|
@[params]
|
||||||
pub struct FsBlobArg {
|
pub struct FsBlobArg {
|
||||||
pub mut:
|
pub mut:
|
||||||
data []u8 @[required]
|
data []u8 @[required]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut blob FsBlob) calculate_hash() {
|
pub fn (mut blob FsBlob) calculate_hash() {
|
||||||
@@ -64,10 +63,6 @@ pub fn (mut self DBFsBlob) new(args FsBlobArg) !FsBlob {
|
|||||||
o.calculate_hash()
|
o.calculate_hash()
|
||||||
|
|
||||||
// Set base fields
|
// Set base fields
|
||||||
o.name = args.name
|
|
||||||
o.description = args.description
|
|
||||||
o.tags = self.db.tags_get(args.tags)!
|
|
||||||
o.comments = self.db.comments_get(args.comments)!
|
|
||||||
o.updated_at = ourtime.now().unix()
|
o.updated_at = ourtime.now().unix()
|
||||||
|
|
||||||
return o
|
return o
|
||||||
@@ -90,7 +85,6 @@ pub fn (mut self DBFsBlob) set(o FsBlob) !u32 {
|
|||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn (mut self DBFsBlob) delete(id u32) ! {
|
pub fn (mut self DBFsBlob) delete(id u32) ! {
|
||||||
// Get the blob to retrieve its hash
|
// Get the blob to retrieve its hash
|
||||||
mut blob := self.get(id)!
|
mut blob := self.get(id)!
|
||||||
@@ -102,10 +96,25 @@ pub fn (mut self DBFsBlob) delete(id u32) ! {
|
|||||||
self.db.delete[FsBlob](id)!
|
self.db.delete[FsBlob](id)!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn (mut self DBFsBlob) delete_multi(ids []u32) ! {
|
||||||
|
for id in ids {
|
||||||
|
self.delete(id)!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn (mut self DBFsBlob) exist(id u32) !bool {
|
pub fn (mut self DBFsBlob) exist(id u32) !bool {
|
||||||
return self.db.exists[FsBlob](id)!
|
return self.db.exists[FsBlob](id)!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn (mut self DBFsBlob) exist_multi(ids []u32) !bool {
|
||||||
|
for id in ids {
|
||||||
|
if !self.exist(id)! {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
pub fn (mut self DBFsBlob) get(id u32) !FsBlob {
|
pub fn (mut self DBFsBlob) get(id u32) !FsBlob {
|
||||||
mut o, data := self.db.get_data[FsBlob](id)!
|
mut o, data := self.db.get_data[FsBlob](id)!
|
||||||
mut e_decoder := encoder.decoder_new(data)
|
mut e_decoder := encoder.decoder_new(data)
|
||||||
@@ -113,17 +122,24 @@ pub fn (mut self DBFsBlob) get(id u32) !FsBlob {
|
|||||||
return o
|
return o
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut self DBFsBlob) get_by_hash(hash string) !FsBlob {
|
pub fn (mut self DBFsBlob) get_multi(id []u32) ![]FsBlob {
|
||||||
id_str := self.db.redis.hget('fsblob:hashes', hash)!
|
mut blobs := []FsBlob{}
|
||||||
if id_str == '' {
|
for i in id {
|
||||||
return error('Blob with hash "${hash}" not found')
|
blobs << self.get(i)!
|
||||||
}
|
}
|
||||||
return self.get(id_str.u32())!
|
return blobs
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (mut self DBFsBlob) get_by_hash(hash string) !FsBlob {
|
||||||
|
if self.factory.fs_blob_membership.exist(hash)! {
|
||||||
|
o := self.factory.fs_blob_membership.get(hash) or { panic('bug') }
|
||||||
|
return self.get(o.blobid)!
|
||||||
|
}
|
||||||
|
return error('Blob with hash ${hash} not found')
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut self DBFsBlob) exists_by_hash(hash string) !bool {
|
pub fn (mut self DBFsBlob) exists_by_hash(hash string) !bool {
|
||||||
id_str := self.db.redis.hget('fsblob:hashes', hash)!
|
return self.factory.fs_blob_membership.exist(hash)
|
||||||
return id_str != ''
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (blob FsBlob) verify_integrity() bool {
|
pub fn (blob FsBlob) verify_integrity() bool {
|
||||||
@@ -131,16 +147,7 @@ pub fn (blob FsBlob) verify_integrity() bool {
|
|||||||
return hash.hex()[..48] == blob.hash
|
return hash.hex()[..48] == blob.hash
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify checks the integrity of a blob by its ID or hash
|
pub fn (mut self DBFsBlob) verify(hash string) !bool {
|
||||||
// Returns true if the blob's data matches its stored hash
|
blob := self.get_by_hash(hash)!
|
||||||
pub fn (mut self DBFsBlob) verify(id_or_hash string) !bool {
|
|
||||||
// Try to parse as ID first
|
|
||||||
if id_or_hash.is_int() {
|
|
||||||
blob := self.get(id_or_hash.int().u32())!
|
|
||||||
return blob.verify_integrity()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise treat as hash
|
|
||||||
blob := self.get_by_hash(id_or_hash)!
|
|
||||||
return blob.verify_integrity()
|
return blob.verify_integrity()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,21 @@
|
|||||||
module herofs
|
module herofs
|
||||||
|
|
||||||
import time
|
|
||||||
import crypto.blake3
|
|
||||||
import freeflowuniverse.herolib.data.encoder
|
import freeflowuniverse.herolib.data.encoder
|
||||||
import freeflowuniverse.herolib.data.ourtime
|
|
||||||
import freeflowuniverse.herolib.hero.db
|
import freeflowuniverse.herolib.hero.db
|
||||||
|
|
||||||
|
// FsBlobMembership represents membership of a blob in one or more filesystems, the key is the hash of the blob
|
||||||
//FsBlobMembership represents membership of a blob in one or more filesystems, the key is the hash of the blob
|
|
||||||
@[heap]
|
@[heap]
|
||||||
pub struct FsBlobMembership {
|
pub struct FsBlobMembership {
|
||||||
pub mut:
|
pub mut:
|
||||||
hash string // blake192 hash of content
|
hash string // blake192 hash of content
|
||||||
fsid []u32 //list of fs ids where this blob is used
|
fsid []u32 // list of fs ids where this blob is used
|
||||||
blobid u32 // id of the blob
|
blobid u32 // id of the blob
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DBFsBlobMembership {
|
pub struct DBFsBlobMembership {
|
||||||
pub mut:
|
pub mut:
|
||||||
db &db.DB @[skip; str: skip]
|
db &db.DB @[skip; str: skip]
|
||||||
|
factory &FsFactory = unsafe { nil } @[skip; str: skip]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (self FsBlobMembership) type_name() string {
|
pub fn (self FsBlobMembership) type_name() string {
|
||||||
@@ -40,9 +37,9 @@ fn (mut self DBFsBlobMembership) load(mut o FsBlobMembership, mut e encoder.Deco
|
|||||||
@[params]
|
@[params]
|
||||||
pub struct FsBlobMembershipArg {
|
pub struct FsBlobMembershipArg {
|
||||||
pub mut:
|
pub mut:
|
||||||
hash string @[required]
|
hash string @[required]
|
||||||
fsid []u32 @[required]
|
fsid []u32 @[required]
|
||||||
blobid u32 @[required]
|
blobid u32 @[required]
|
||||||
}
|
}
|
||||||
|
|
||||||
// get new blob membership, not from the DB
|
// get new blob membership, not from the DB
|
||||||
@@ -58,21 +55,21 @@ pub fn (mut self DBFsBlobMembership) new(args FsBlobMembershipArg) !FsBlobMember
|
|||||||
|
|
||||||
pub fn (mut self DBFsBlobMembership) set(o FsBlobMembership) !string {
|
pub fn (mut self DBFsBlobMembership) set(o FsBlobMembership) !string {
|
||||||
// Validate that the blob exists
|
// Validate that the blob exists
|
||||||
blob_exists := self.db.fs_blob.exists(o.blobid)!
|
blob_exists := self.factory.fs_blob.exists(o.blobid)!
|
||||||
if !blob_exists {
|
if !blob_exists {
|
||||||
return error('Blob with ID ${o.blobid} does not exist')
|
return error('Blob with ID ${o.blobid} does not exist')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate that all filesystems exist
|
// Validate that all filesystems exist
|
||||||
for fs_id in o.fsid {
|
for fs_id in o.fsid {
|
||||||
fs_exists := self.db.fs_file.exists(fs_id)!
|
fs_exists := self.factory.fs_file.exists(fs_id)!
|
||||||
if !fs_exists {
|
if !fs_exists {
|
||||||
return error('Filesystem with ID ${fs_id} does not exist')
|
return error('Filesystem with ID ${fs_id} does not exist')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode the object
|
// Encode the object
|
||||||
mut e_encoder := encoder.encoder_new()
|
mut e_encoder := encoder.new()
|
||||||
o.dump(mut e_encoder)!
|
o.dump(mut e_encoder)!
|
||||||
|
|
||||||
// Store using hash as key in the blob_membership hset
|
// Store using hash as key in the blob_membership hset
|
||||||
@@ -85,8 +82,7 @@ pub fn (mut self DBFsBlobMembership) delete(hash string) ! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut self DBFsBlobMembership) exist(hash string) !bool {
|
pub fn (mut self DBFsBlobMembership) exist(hash string) !bool {
|
||||||
result := self.db.redis.hexists('fs_blob_membership', hash)!
|
return self.db.redis.hexists('fs_blob_membership', hash)!
|
||||||
return result == 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut self DBFsBlobMembership) get(hash string) !FsBlobMembership {
|
pub fn (mut self DBFsBlobMembership) get(hash string) !FsBlobMembership {
|
||||||
@@ -97,11 +93,11 @@ pub fn (mut self DBFsBlobMembership) get(hash string) !FsBlobMembership {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Decode hex data back to bytes
|
// Decode hex data back to bytes
|
||||||
data := data.bytes()
|
data2 := data.bytes()
|
||||||
|
|
||||||
// Create object and decode
|
// Create object and decode
|
||||||
mut o := FsBlobMembership{}
|
mut o := FsBlobMembership{}
|
||||||
mut e_decoder := encoder.decoder_new(data)
|
mut e_decoder := encoder.decoder_new(data2)
|
||||||
self.load(mut o, mut e_decoder)!
|
self.load(mut o, mut e_decoder)!
|
||||||
|
|
||||||
return o
|
return o
|
||||||
@@ -116,34 +112,31 @@ pub fn (mut self DBFsBlobMembership) add_filesystem(hash string, fs_id u32) !str
|
|||||||
}
|
}
|
||||||
|
|
||||||
mut membership := self.get(hash)!
|
mut membership := self.get(hash)!
|
||||||
|
|
||||||
// Check if filesystem is already in the list
|
// Check if filesystem is already in the list
|
||||||
if fs_id !in membership.fsid {
|
if fs_id !in membership.fsid {
|
||||||
membership.fsid << fs_id
|
membership.fsid << fs_id
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.set(membership)!
|
return self.set(membership)!
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove a filesystem from an existing blob membership
|
// Remove a filesystem from an existing blob membership
|
||||||
pub fn (mut self DBFsBlobMembership) remove_filesystem(hash string, fs_id u32) !string {
|
pub fn (mut self DBFsBlobMembership) remove_filesystem(hash string, fs_id u32) !string {
|
||||||
mut membership := self.get(hash)!
|
mut membership := self.get(hash)!
|
||||||
|
|
||||||
// Remove filesystem from the list
|
// Remove filesystem from the list
|
||||||
membership.fsid = membership.fsid.filter(it != fs_id)
|
membership.fsid = membership.fsid.filter(it != fs_id)
|
||||||
|
|
||||||
// If no filesystems left, delete the membership entirely
|
// If no filesystems left, delete the membership entirely
|
||||||
if membership.fsid.len == 0 {
|
if membership.fsid.len == 0 {
|
||||||
self.delete(hash)!
|
self.delete(hash)!
|
||||||
return hash
|
return hash
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.set(membership)!
|
return self.set(membership)!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// BlobList represents a simplified blob structure for listing purposes
|
// BlobList represents a simplified blob structure for listing purposes
|
||||||
pub struct BlobList {
|
pub struct BlobList {
|
||||||
pub mut:
|
pub mut:
|
||||||
@@ -158,18 +151,18 @@ pub fn (mut self DBFsBlobMembership) list(prefix string) ![]FsBlobMembership {
|
|||||||
mut result := []FsBlobMembership{}
|
mut result := []FsBlobMembership{}
|
||||||
mut cursor := 0
|
mut cursor := 0
|
||||||
mut count := 0
|
mut count := 0
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if count >= 10000 {
|
if count >= 10000 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use hscan with MATCH pattern and COUNT to iterate through the hash
|
// Use hscan with MATCH pattern and COUNT to iterate through the hash
|
||||||
new_cursor, values := self.db.redis.hscan('fs_blob_membership', cursor,
|
new_cursor, values := self.db.redis.hscan('fs_blob_membership', cursor,
|
||||||
match: '${prefix}*',
|
match: '${prefix}*'
|
||||||
count: 100
|
count: 100
|
||||||
)!
|
)!
|
||||||
|
|
||||||
// Process the returned field-value pairs
|
// Process the returned field-value pairs
|
||||||
// hscan returns alternating field-value pairs, so we iterate by 2
|
// hscan returns alternating field-value pairs, so we iterate by 2
|
||||||
mut i := 0
|
mut i := 0
|
||||||
@@ -177,20 +170,20 @@ pub fn (mut self DBFsBlobMembership) list(prefix string) ![]FsBlobMembership {
|
|||||||
hash := values[i]
|
hash := values[i]
|
||||||
// Skip the value (we don't need it since we'll get the object by hash)
|
// Skip the value (we don't need it since we'll get the object by hash)
|
||||||
i += 2
|
i += 2
|
||||||
|
|
||||||
if hash.starts_with(prefix) {
|
if hash.starts_with(prefix) {
|
||||||
result << self.get(hash)!
|
result << self.get(hash)!
|
||||||
count++
|
count++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If cursor is "0", we've completed the full iteration
|
// If cursor is "0", we've completed the full iteration
|
||||||
if new_cursor == '0' {
|
if new_cursor == '0' {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
cursor = new_cursor.int()
|
cursor = new_cursor.int()
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ import freeflowuniverse.herolib.hero.db
|
|||||||
pub struct FsDir {
|
pub struct FsDir {
|
||||||
db.Base
|
db.Base
|
||||||
pub mut:
|
pub mut:
|
||||||
fs_id u32 // Associated filesystem
|
fs_id u32 // Associated filesystem
|
||||||
parent_id u32 // Parent directory ID (0 for root)
|
parent_id u32 // Parent directory ID (0 for root)
|
||||||
directories []u32
|
directories []u32
|
||||||
files []u32
|
files []u32
|
||||||
symlinks []u32
|
symlinks []u32
|
||||||
@@ -21,7 +21,8 @@ pub mut:
|
|||||||
|
|
||||||
pub struct DBFsDir {
|
pub struct DBFsDir {
|
||||||
pub mut:
|
pub mut:
|
||||||
db &db.DB @[skip; str: skip]
|
db &db.DB @[skip; str: skip]
|
||||||
|
factory &FsFactory = unsafe { nil } @[skip; str: skip]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (self FsDir) type_name() string {
|
pub fn (self FsDir) type_name() string {
|
||||||
@@ -29,22 +30,21 @@ pub fn (self FsDir) type_name() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn (self FsDir) dump(mut e encoder.Encoder) ! {
|
pub fn (self FsDir) dump(mut e encoder.Encoder) ! {
|
||||||
|
|
||||||
e.add_u32(self.fs_id)
|
e.add_u32(self.fs_id)
|
||||||
e.add_u32(self.parent_id)
|
e.add_u32(self.parent_id)
|
||||||
|
|
||||||
// Handle directories array
|
// Handle directories array
|
||||||
e.add_u16(u16(self.directories.len))
|
e.add_u16(u16(self.directories.len))
|
||||||
for dir_id in self.directories {
|
for dir_id in self.directories {
|
||||||
e.add_u32(dir_id)
|
e.add_u32(dir_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle files array
|
// Handle files array
|
||||||
e.add_u16(u16(self.files.len))
|
e.add_u16(u16(self.files.len))
|
||||||
for file_id in self.files {
|
for file_id in self.files {
|
||||||
e.add_u32(file_id)
|
e.add_u32(file_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle symlinks array
|
// Handle symlinks array
|
||||||
e.add_u16(u16(self.symlinks.len))
|
e.add_u16(u16(self.symlinks.len))
|
||||||
for symlink_id in self.symlinks {
|
for symlink_id in self.symlinks {
|
||||||
@@ -55,21 +55,21 @@ pub fn (self FsDir) dump(mut e encoder.Encoder) ! {
|
|||||||
fn (mut self DBFsDir) load(mut o FsDir, mut e encoder.Decoder) ! {
|
fn (mut self DBFsDir) load(mut o FsDir, mut e encoder.Decoder) ! {
|
||||||
o.fs_id = e.get_u32()!
|
o.fs_id = e.get_u32()!
|
||||||
o.parent_id = e.get_u32()!
|
o.parent_id = e.get_u32()!
|
||||||
|
|
||||||
// Load directories array
|
// Load directories array
|
||||||
directories_count := e.get_u16()!
|
directories_count := e.get_u16()!
|
||||||
o.directories = []u32{cap: int(directories_count)}
|
o.directories = []u32{cap: int(directories_count)}
|
||||||
for _ in 0 .. directories_count {
|
for _ in 0 .. directories_count {
|
||||||
o.directories << e.get_u32()!
|
o.directories << e.get_u32()!
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load files array
|
// Load files array
|
||||||
files_count := e.get_u16()!
|
files_count := e.get_u16()!
|
||||||
o.files = []u32{cap: int(files_count)}
|
o.files = []u32{cap: int(files_count)}
|
||||||
for _ in 0 .. files_count {
|
for _ in 0 .. files_count {
|
||||||
o.files << e.get_u32()!
|
o.files << e.get_u32()!
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load symlinks array
|
// Load symlinks array
|
||||||
symlinks_count := e.get_u16()!
|
symlinks_count := e.get_u16()!
|
||||||
o.symlinks = []u32{cap: int(symlinks_count)}
|
o.symlinks = []u32{cap: int(symlinks_count)}
|
||||||
@@ -121,14 +121,14 @@ pub fn (mut self DBFsDir) set(o FsDir) !u32 {
|
|||||||
pub fn (mut self DBFsDir) delete(id u32) ! {
|
pub fn (mut self DBFsDir) delete(id u32) ! {
|
||||||
// Get the directory info before deleting
|
// Get the directory info before deleting
|
||||||
dir := self.get(id)!
|
dir := self.get(id)!
|
||||||
|
|
||||||
// If has parent, remove from parent's directories list
|
// If has parent, remove from parent's directories list
|
||||||
if dir.parent_id > 0 {
|
if dir.parent_id > 0 {
|
||||||
mut parent_dir := self.db.directories.get(dir.parent_id) or {
|
mut parent_dir := self.factory.fs_dir.get(dir.parent_id) or {
|
||||||
return error('Parent directory with ID ${dir.parent_id} does not exist')
|
return error('Parent directory with ID ${dir.parent_id} does not exist')
|
||||||
}
|
}
|
||||||
parent_dir.directories = parent_dir.directories.filter(it != id)
|
parent_dir.directories = parent_dir.directories.filter(it != id)
|
||||||
self.db.directories.set(parent_dir)!
|
self.factory.fs_dir.set(parent_dir)!
|
||||||
}
|
}
|
||||||
// Delete the directory itself
|
// Delete the directory itself
|
||||||
self.db.delete[FsDir](id)!
|
self.db.delete[FsDir](id)!
|
||||||
|
|||||||
@@ -18,14 +18,15 @@ pub mut:
|
|||||||
blobs []u32 // IDs of file content blobs
|
blobs []u32 // IDs of file content blobs
|
||||||
size_bytes u64
|
size_bytes u64
|
||||||
mime_type MimeType // MIME type as enum (MOVED FROM FsBlob)
|
mime_type MimeType // MIME type as enum (MOVED FROM FsBlob)
|
||||||
checksum string // e.g., SHA256 checksum of the file
|
checksum string // e.g., SHA256 checksum of the file
|
||||||
accessed_at i64
|
accessed_at i64
|
||||||
metadata map[string]string // Custom metadata
|
metadata map[string]string // Custom metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DBFsFile {
|
pub struct DBFsFile {
|
||||||
pub mut:
|
pub mut:
|
||||||
db &db.DB @[skip; str: skip]
|
db &db.DB @[skip; str: skip]
|
||||||
|
factory &FsFactory = unsafe { nil } @[skip; str: skip]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (self FsFile) type_name() string {
|
pub fn (self FsFile) type_name() string {
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ pub enum SymlinkTargetType {
|
|||||||
|
|
||||||
pub struct DBFsSymlink {
|
pub struct DBFsSymlink {
|
||||||
pub mut:
|
pub mut:
|
||||||
db &db.DB @[skip; str: skip]
|
db &db.DB @[skip; str: skip]
|
||||||
|
factory &FsFactory = unsafe { nil } @[skip; str: skip]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (self FsSymlink) type_name() string {
|
pub fn (self FsSymlink) type_name() string {
|
||||||
|
|||||||
Reference in New Issue
Block a user