Files
herolib/lib/vfs/vfs_contacts/vfs_implementation.v
Mahmoud Emad abd694015b feat: Add contacts database and VFS implementation
- Added a new contacts database (`ContactsDB`) to store contact
  information.  This improves data organization and allows for
  more efficient querying and manipulation of contact data.
- Implemented a virtual file system (VFS) for contacts
  (`vfs_contacts`). This provides a file-like interface to access
  and manage contact data, improving integration with existing
  file-system-based tools and workflows.  The VFS supports
  listing by group, by name, and by email.
- Added model structs for contacts, improving data organization and
  serialization.  This lays the foundation for more robust data
  handling and future expansion.
2025-03-17 15:58:20 +02:00

411 lines
10 KiB
V

module vfs_contacts
import json
import time
import freeflowuniverse.herolib.vfs
import freeflowuniverse.herolib.circles.mcc.models as contacts
import freeflowuniverse.herolib.core.texttools
// Basic operations
pub fn (mut myvfs ContactsVFS) root_get() !vfs.FSEntry {
metadata := vfs.Metadata{
id: 1
name: ''
file_type: .directory
created_at: time.now().unix()
modified_at: time.now().unix()
accessed_at: time.now().unix()
}
return ContactsFSEntry{
path: ''
metadata: metadata
}
}
// File operations
pub fn (mut myvfs ContactsVFS) file_create(path string) !vfs.FSEntry {
return error('Contacts VFS is read-only')
}
pub fn (mut myvfs ContactsVFS) file_read(path string) ![]u8 {
if !myvfs.exists(path) {
return error('File does not exist: ${path}')
}
entry := myvfs.get(path)!
if !entry.is_file() {
return error('Path is not a file: ${path}')
}
contacts_entry := entry as ContactsFSEntry
if contact := contacts_entry.contact {
return json.encode(contact).bytes()
}
return error('Failed to read file: ${path}')
}
pub fn (mut myvfs ContactsVFS) file_write(path string, data []u8) ! {
return error('Contacts VFS is read-only')
}
pub fn (mut myvfs ContactsVFS) file_concatenate(path string, data []u8) ! {
return error('Contacts VFS is read-only')
}
pub fn (mut myvfs ContactsVFS) file_delete(path string) ! {
return error('Contacts VFS is read-only')
}
// Directory operations
pub fn (mut myvfs ContactsVFS) dir_create(path string) !vfs.FSEntry {
return error('Contacts VFS is read-only')
}
pub fn (mut myvfs ContactsVFS) dir_list(path string) ![]vfs.FSEntry {
if !myvfs.exists(path) {
return error('Directory does not exist: ${path}')
}
// Get all contacts
contacts_ := myvfs.contacts_db.getall() or { return error('Failed to get contacts: ${err}') }
// If we're at the root, return all groups
if path == '' {
return myvfs.list_groups(contacts_)!
}
// Check if we're in a group path
path_parts := path.split('/')
if path_parts.len == 1 {
// We're in a group, show the by_name and by_email directories
return myvfs.list_group_subdirs(path)!
} else if path_parts.len == 2 && path_parts[1] in ['by_name', 'by_email'] {
// We're in a by_name or by_email directory, list the contacts
return myvfs.list_contacts_by_type(path_parts[0], path_parts[1], contacts_)!
}
return []vfs.FSEntry{}
}
pub fn (mut myvfs ContactsVFS) dir_delete(path string) ! {
return error('Contacts VFS is read-only')
}
// Symlink operations
pub fn (mut myvfs ContactsVFS) link_create(target_path string, link_path string) !vfs.FSEntry {
return error('Contacts VFS does not support symlinks')
}
pub fn (mut myvfs ContactsVFS) link_read(path string) !string {
return error('Contacts VFS does not support symlinks')
}
pub fn (mut myvfs ContactsVFS) link_delete(path string) ! {
return error('Contacts VFS does not support symlinks')
}
// Common operations
pub fn (mut myvfs ContactsVFS) exists(path string) bool {
// Root always exists
if path == '' {
return true
}
// Get all contacts
contacts_ := myvfs.contacts_db.getall() or { return false }
path_parts := path.split('/')
// Check if the path is a group
if path_parts.len == 1 {
for contact in contacts_ {
if contact.group == path_parts[0] {
return true
}
}
}
// Check if the path is a group subdir (by_name or by_email)
if path_parts.len == 2 && path_parts[1] in ['by_name', 'by_email'] {
for contact in contacts_ {
if contact.group == path_parts[0] {
return true
}
}
}
// Check if the path is a contact file
if path_parts.len == 3 && path_parts[1] in ['by_name', 'by_email'] {
for contact in contacts_ {
if contact.group != path_parts[0] {
continue
}
if path_parts[1] == 'by_name' {
filename := texttools.name_fix('${contact.first_name}_${contact.last_name}') +
'.json'
if filename == path_parts[2] {
return true
}
} else if path_parts[1] == 'by_email' {
filename := texttools.name_fix(contact.email) + '.json'
if filename == path_parts[2] {
return true
}
}
}
}
return false
}
pub fn (mut myvfs ContactsVFS) get(path string) !vfs.FSEntry {
// Root always exists
if path == '' {
return myvfs.root_get()!
}
// Get all contacts
contacts_ := myvfs.contacts_db.getall() or { return error('Failed to get contacts: ${err}') }
path_parts := path.split('/')
// Check if the path is a group
if path_parts.len == 1 {
for contact in contacts_ {
if contact.group == path_parts[0] {
metadata := vfs.Metadata{
id: u32(path_parts[0].bytes().bytestr().hash())
name: path_parts[0]
file_type: .directory
created_at: time.now().unix()
modified_at: time.now().unix()
accessed_at: time.now().unix()
}
return ContactsFSEntry{
path: path
metadata: metadata
}
}
}
}
// Check if the path is a group subdir (by_name or by_email)
if path_parts.len == 2 && path_parts[1] in ['by_name', 'by_email'] {
metadata := vfs.Metadata{
id: u32(path.bytes().bytestr().hash())
name: path_parts[1]
file_type: .directory
created_at: time.now().unix()
modified_at: time.now().unix()
accessed_at: time.now().unix()
}
return ContactsFSEntry{
path: path
metadata: metadata
}
}
// Check if the path is a contact file
if path_parts.len == 3 && path_parts[1] in ['by_name', 'by_email'] {
for contact in contacts_ {
if contact.group != path_parts[0] {
continue
}
if path_parts[1] == 'by_name' {
filename := texttools.name_fix('${contact.first_name}_${contact.last_name}') +
'.json'
if filename == path_parts[2] {
metadata := vfs.Metadata{
id: u32(contact.id)
name: filename
file_type: .file
size: u64(json.encode(contact).len)
created_at: contact.created_at
modified_at: contact.modified_at
accessed_at: time.now().unix()
}
return ContactsFSEntry{
path: path
metadata: metadata
contact: contact
}
}
} else if path_parts[1] == 'by_email' {
filename := texttools.name_fix(contact.email) + '.json'
if filename == path_parts[2] {
metadata := vfs.Metadata{
id: u32(contact.id)
name: filename
file_type: .file
size: u64(json.encode(contact).len)
created_at: contact.created_at
modified_at: contact.modified_at
accessed_at: time.now().unix()
}
return ContactsFSEntry{
path: path
metadata: metadata
contact: contact
}
}
}
}
}
return error('Path not found: ${path}')
}
pub fn (mut myvfs ContactsVFS) rename(old_path string, new_path string) !vfs.FSEntry {
return error('Contacts VFS is read-only')
}
pub fn (mut myvfs ContactsVFS) copy(src_path string, dst_path string) !vfs.FSEntry {
return error('Contacts VFS is read-only')
}
pub fn (mut myvfs ContactsVFS) move(src_path string, dst_path string) !vfs.FSEntry {
return error('Contacts VFS is read-only')
}
pub fn (mut myvfs ContactsVFS) delete(path string) ! {
return error('Contacts VFS is read-only')
}
// FSEntry Operations
pub fn (mut myvfs ContactsVFS) get_path(entry &vfs.FSEntry) !string {
contacts_entry := entry as ContactsFSEntry
return contacts_entry.path
}
pub fn (mut myvfs ContactsVFS) print() ! {
println('Contacts VFS')
}
// Cleanup operation
pub fn (mut myvfs ContactsVFS) destroy() ! {
// Nothing to clean up
}
// Helper functions
fn (mut myvfs ContactsVFS) list_groups(contacts_ []contacts.Contact) ![]vfs.FSEntry {
mut groups := map[string]bool{}
// Collect unique group names
for contact in contacts_ {
groups[contact.group] = true
}
// Create FSEntry for each group
mut result := []vfs.FSEntry{cap: groups.len}
for group, _ in groups {
metadata := vfs.Metadata{
id: u32(group.bytes().bytestr().hash())
name: group
file_type: .directory
created_at: time.now().unix()
modified_at: time.now().unix()
accessed_at: time.now().unix()
}
result << ContactsFSEntry{
path: group
metadata: metadata
}
}
return result
}
fn (mut myvfs ContactsVFS) list_group_subdirs(group string) ![]vfs.FSEntry {
mut result := []vfs.FSEntry{cap: 2}
// Create by_name directory
by_name_metadata := vfs.Metadata{
id: u32('${group}/by_name'.bytes().bytestr().hash())
name: 'by_name'
file_type: .directory
created_at: time.now().unix()
modified_at: time.now().unix()
accessed_at: time.now().unix()
}
result << ContactsFSEntry{
path: '${group}/by_name'
metadata: by_name_metadata
}
// Create by_email directory
by_email_metadata := vfs.Metadata{
id: u32('${group}/by_email'.bytes().bytestr().hash())
name: 'by_email'
file_type: .directory
created_at: time.now().unix()
modified_at: time.now().unix()
accessed_at: time.now().unix()
}
result << ContactsFSEntry{
path: '${group}/by_email'
metadata: by_email_metadata
}
return result
}
fn (mut myvfs ContactsVFS) list_contacts_by_type(group string, list_type string, contacts_ []contacts.Contact) ![]vfs.FSEntry {
mut result := []vfs.FSEntry{}
for contact in contacts_ {
if contact.group != group {
continue
}
if list_type == 'by_name' {
filename := texttools.name_fix('${contact.first_name}_${contact.last_name}') + '.json'
metadata := vfs.Metadata{
id: u32(contact.id)
name: filename
file_type: .file
size: u64(json.encode(contact).len)
created_at: contact.created_at
modified_at: contact.modified_at
accessed_at: time.now().unix()
}
result << ContactsFSEntry{
path: '${group}/by_name/${filename}'
metadata: metadata
contact: contact
}
} else if list_type == 'by_email' {
filename := texttools.name_fix(contact.email) + '.json'
metadata := vfs.Metadata{
id: u32(contact.id)
name: filename
file_type: .file
size: u64(json.encode(contact).len)
created_at: contact.created_at
modified_at: contact.modified_at
accessed_at: time.now().unix()
}
result << ContactsFSEntry{
path: '${group}/by_email/${filename}'
metadata: metadata
contact: contact
}
}
}
return result
}