...
This commit is contained in:
9
lib/vfs/vfs_mail/factory.v
Normal file
9
lib/vfs/vfs_mail/factory.v
Normal file
@@ -0,0 +1,9 @@
|
||||
module vfs_mail
|
||||
|
||||
import freeflowuniverse.herolib.vfs
|
||||
import freeflowuniverse.herolib.circles.dbs.core
|
||||
|
||||
// new creates a new mail VFS instance
|
||||
pub fn new(mail_db &core.MailDB) !vfs.VFSImplementation {
|
||||
return new_mail_vfs(mail_db)!
|
||||
}
|
||||
35
lib/vfs/vfs_mail/model_fsentry.v
Normal file
35
lib/vfs/vfs_mail/model_fsentry.v
Normal file
@@ -0,0 +1,35 @@
|
||||
module vfs_mail
|
||||
|
||||
import freeflowuniverse.herolib.vfs
|
||||
import freeflowuniverse.herolib.circles.models.mail
|
||||
|
||||
// MailFSEntry implements FSEntry for mail objects
|
||||
pub struct MailFSEntry {
|
||||
pub mut:
|
||||
path string
|
||||
metadata vfs.Metadata
|
||||
email ?mail.Email
|
||||
}
|
||||
|
||||
// is_dir returns true if the entry is a directory
|
||||
pub fn (self &MailFSEntry) is_dir() bool {
|
||||
return self.metadata.file_type == .directory
|
||||
}
|
||||
|
||||
// is_file returns true if the entry is a file
|
||||
pub fn (self &MailFSEntry) is_file() bool {
|
||||
return self.metadata.file_type == .file
|
||||
}
|
||||
|
||||
// is_symlink returns true if the entry is a symlink
|
||||
pub fn (self &MailFSEntry) is_symlink() bool {
|
||||
return self.metadata.file_type == .symlink
|
||||
}
|
||||
|
||||
pub fn (e MailFSEntry) get_metadata() vfs.Metadata {
|
||||
return e.metadata
|
||||
}
|
||||
|
||||
pub fn (e MailFSEntry) get_path() string {
|
||||
return e.path
|
||||
}
|
||||
438
lib/vfs/vfs_mail/vfs_implementation.v
Normal file
438
lib/vfs/vfs_mail/vfs_implementation.v
Normal file
@@ -0,0 +1,438 @@
|
||||
module vfs_mail
|
||||
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
import freeflowuniverse.herolib.vfs
|
||||
import freeflowuniverse.herolib.circles.models.mail
|
||||
import freeflowuniverse.herolib.circles.dbs.core
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
|
||||
// Basic operations
|
||||
pub fn (mut myvfs MailVFS) 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 MailFSEntry{
|
||||
path: ''
|
||||
metadata: metadata
|
||||
}
|
||||
}
|
||||
|
||||
// File operations
|
||||
pub fn (mut myvfs MailVFS) file_create(path string) !vfs.FSEntry {
|
||||
return error('Mail VFS is read-only')
|
||||
}
|
||||
|
||||
pub fn (mut myvfs MailVFS) 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}')
|
||||
}
|
||||
|
||||
mail_entry := entry as MailFSEntry
|
||||
if email := mail_entry.email {
|
||||
return json.encode(email).bytes()
|
||||
}
|
||||
|
||||
return error('Failed to read file: ${path}')
|
||||
}
|
||||
|
||||
pub fn (mut myvfs MailVFS) file_write(path string, data []u8) ! {
|
||||
return error('Mail VFS is read-only')
|
||||
}
|
||||
|
||||
pub fn (mut myvfs MailVFS) file_concatenate(path string, data []u8) ! {
|
||||
return error('Mail VFS is read-only')
|
||||
}
|
||||
|
||||
pub fn (mut myvfs MailVFS) file_delete(path string) ! {
|
||||
return error('Mail VFS is read-only')
|
||||
}
|
||||
|
||||
// Directory operations
|
||||
pub fn (mut myvfs MailVFS) dir_create(path string) !vfs.FSEntry {
|
||||
return error('Mail VFS is read-only')
|
||||
}
|
||||
|
||||
pub fn (mut myvfs MailVFS) dir_list(path string) ![]vfs.FSEntry {
|
||||
if !myvfs.exists(path) {
|
||||
return error('Directory does not exist: ${path}')
|
||||
}
|
||||
|
||||
// Get all emails
|
||||
emails := myvfs.mail_db.getall() or { return error('Failed to get emails: ${err}') }
|
||||
|
||||
// If we're at the root, return all mailboxes
|
||||
if path == '' {
|
||||
return myvfs.list_mailboxes(emails)!
|
||||
}
|
||||
|
||||
// Check if we're in a mailbox path
|
||||
path_parts := path.split('/')
|
||||
if path_parts.len == 1 {
|
||||
// We're in a mailbox, show the id and subject directories
|
||||
return myvfs.list_mailbox_subdirs(path)!
|
||||
} else if path_parts.len == 2 && path_parts[1] in ['id', 'subject'] {
|
||||
// We're in an id or subject directory, list the emails
|
||||
return myvfs.list_emails_by_type(path_parts[0], path_parts[1], emails)!
|
||||
}
|
||||
|
||||
return []vfs.FSEntry{}
|
||||
}
|
||||
|
||||
pub fn (mut myvfs MailVFS) dir_delete(path string) ! {
|
||||
return error('Mail VFS is read-only')
|
||||
}
|
||||
|
||||
// Symlink operations
|
||||
pub fn (mut myvfs MailVFS) link_create(target_path string, link_path string) !vfs.FSEntry {
|
||||
return error('Mail VFS does not support symlinks')
|
||||
}
|
||||
|
||||
pub fn (mut myvfs MailVFS) link_read(path string) !string {
|
||||
return error('Mail VFS does not support symlinks')
|
||||
}
|
||||
|
||||
pub fn (mut myvfs MailVFS) link_delete(path string) ! {
|
||||
return error('Mail VFS does not support symlinks')
|
||||
}
|
||||
|
||||
// Common operations
|
||||
pub fn (mut myvfs MailVFS) exists(path string) bool {
|
||||
// Root always exists
|
||||
if path == '' {
|
||||
return true
|
||||
}
|
||||
|
||||
// Get all emails
|
||||
emails := myvfs.mail_db.getall() or { return false }
|
||||
|
||||
// Debug print
|
||||
if path.contains('subject') {
|
||||
println('Checking exists for path: ${path}')
|
||||
}
|
||||
|
||||
path_parts := path.split('/')
|
||||
|
||||
// Check if the path is a mailbox
|
||||
if path_parts.len == 1 {
|
||||
for email in emails {
|
||||
mailbox_parts := email.mailbox.split('/')
|
||||
if mailbox_parts.len > 0 && mailbox_parts[0] == path_parts[0] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the path is a mailbox subdir (id or subject)
|
||||
if path_parts.len == 2 && path_parts[1] in ['id', 'subject'] {
|
||||
for email in emails {
|
||||
mailbox_parts := email.mailbox.split('/')
|
||||
if mailbox_parts.len > 0 && mailbox_parts[0] == path_parts[0] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the path is an email file
|
||||
if path_parts.len == 3 && path_parts[1] in ['id', 'subject'] {
|
||||
for email in emails {
|
||||
if email.mailbox.split('/')[0] != path_parts[0] {
|
||||
continue
|
||||
}
|
||||
|
||||
if path_parts[1] == 'id' && '${email.id}.json' == path_parts[2] {
|
||||
return true
|
||||
} else if path_parts[1] == 'subject' {
|
||||
if envelope := email.envelope {
|
||||
subject_filename := texttools.name_fix(envelope.subject) + '.json'
|
||||
if path.contains('subject') {
|
||||
println('Comparing: "${path_parts[2]}" with "${subject_filename}"')
|
||||
println('Original subject: "${envelope.subject}"')
|
||||
println('After name_fix: "${texttools.name_fix(envelope.subject)}"')
|
||||
}
|
||||
if subject_filename == path_parts[2] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
pub fn (mut myvfs MailVFS) get(path string) !vfs.FSEntry {
|
||||
// Root always exists
|
||||
if path == '' {
|
||||
return myvfs.root_get()!
|
||||
}
|
||||
|
||||
// Debug print
|
||||
println('Getting path: ${path}')
|
||||
|
||||
// Get all emails
|
||||
emails := myvfs.mail_db.getall() or { return error('Failed to get emails: ${err}') }
|
||||
|
||||
// Debug: Print all emails
|
||||
println('All emails in DB:')
|
||||
for email in emails {
|
||||
if envelope := email.envelope {
|
||||
println('Email ID: ${email.id}, Subject: "${envelope.subject}", Mailbox: ${email.mailbox}')
|
||||
}
|
||||
}
|
||||
|
||||
path_parts := path.split('/')
|
||||
|
||||
// Check if the path is a mailbox
|
||||
if path_parts.len == 1 {
|
||||
for email in emails {
|
||||
mailbox_parts := email.mailbox.split('/')
|
||||
if mailbox_parts.len > 0 && mailbox_parts[0] == 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 MailFSEntry{
|
||||
path: path
|
||||
metadata: metadata
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the path is a mailbox subdir (id or subject)
|
||||
if path_parts.len == 2 && path_parts[1] in ['id', 'subject'] {
|
||||
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 MailFSEntry{
|
||||
path: path
|
||||
metadata: metadata
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the path is an email file
|
||||
if path_parts.len == 3 && path_parts[1] in ['id', 'subject'] {
|
||||
for email in emails {
|
||||
if email.mailbox.split('/')[0] != path_parts[0] {
|
||||
continue
|
||||
}
|
||||
|
||||
if path_parts[1] == 'id' && '${email.id}.json' == path_parts[2] {
|
||||
metadata := vfs.Metadata{
|
||||
id: email.id
|
||||
name: '${email.id}.json'
|
||||
file_type: .file
|
||||
size: u64(json.encode(email).len)
|
||||
created_at: email.internal_date
|
||||
modified_at: email.internal_date
|
||||
accessed_at: time.now().unix()
|
||||
}
|
||||
|
||||
return MailFSEntry{
|
||||
path: path
|
||||
metadata: metadata
|
||||
email: email
|
||||
}
|
||||
} else if path_parts[1] == 'subject' {
|
||||
if envelope := email.envelope {
|
||||
subject_filename := texttools.name_fix(envelope.subject) + '.json'
|
||||
if subject_filename == path_parts[2] {
|
||||
metadata := vfs.Metadata{
|
||||
id: email.id
|
||||
name: subject_filename
|
||||
file_type: .file
|
||||
size: u64(json.encode(email).len)
|
||||
created_at: email.internal_date
|
||||
modified_at: email.internal_date
|
||||
accessed_at: time.now().unix()
|
||||
}
|
||||
|
||||
return MailFSEntry{
|
||||
path: path
|
||||
metadata: metadata
|
||||
email: email
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return error('Path not found: ${path}')
|
||||
}
|
||||
|
||||
pub fn (mut myvfs MailVFS) rename(old_path string, new_path string) !vfs.FSEntry {
|
||||
return error('Mail VFS is read-only')
|
||||
}
|
||||
|
||||
pub fn (mut myvfs MailVFS) copy(src_path string, dst_path string) !vfs.FSEntry {
|
||||
return error('Mail VFS is read-only')
|
||||
}
|
||||
|
||||
pub fn (mut myvfs MailVFS) move(src_path string, dst_path string) !vfs.FSEntry {
|
||||
return error('Mail VFS is read-only')
|
||||
}
|
||||
|
||||
pub fn (mut myvfs MailVFS) delete(path string) ! {
|
||||
return error('Mail VFS is read-only')
|
||||
}
|
||||
|
||||
// FSEntry Operations
|
||||
pub fn (mut myvfs MailVFS) get_path(entry &vfs.FSEntry) !string {
|
||||
mail_entry := entry as MailFSEntry
|
||||
return mail_entry.path
|
||||
}
|
||||
|
||||
pub fn (mut myvfs MailVFS) print() ! {
|
||||
println('Mail VFS')
|
||||
}
|
||||
|
||||
// Cleanup operation
|
||||
pub fn (mut myvfs MailVFS) destroy() ! {
|
||||
// Nothing to clean up
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
fn (mut myvfs MailVFS) list_mailboxes(emails []mail.Email) ![]vfs.FSEntry {
|
||||
mut mailboxes := map[string]bool{}
|
||||
|
||||
// Collect unique top-level mailbox names
|
||||
for email in emails {
|
||||
mailbox_parts := email.mailbox.split('/')
|
||||
if mailbox_parts.len > 0 {
|
||||
mailboxes[mailbox_parts[0]] = true
|
||||
}
|
||||
}
|
||||
|
||||
// Create FSEntry for each mailbox
|
||||
mut result := []vfs.FSEntry{cap: mailboxes.len}
|
||||
for mailbox, _ in mailboxes {
|
||||
metadata := vfs.Metadata{
|
||||
id: u32(mailbox.bytes().bytestr().hash())
|
||||
name: mailbox
|
||||
file_type: .directory
|
||||
created_at: time.now().unix()
|
||||
modified_at: time.now().unix()
|
||||
accessed_at: time.now().unix()
|
||||
}
|
||||
|
||||
result << MailFSEntry{
|
||||
path: mailbox
|
||||
metadata: metadata
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
fn (mut myvfs MailVFS) list_mailbox_subdirs(mailbox string) ![]vfs.FSEntry {
|
||||
mut result := []vfs.FSEntry{cap: 2}
|
||||
|
||||
// Create id directory
|
||||
id_metadata := vfs.Metadata{
|
||||
id: u32('${mailbox}/id'.bytes().bytestr().hash())
|
||||
name: 'id'
|
||||
file_type: .directory
|
||||
created_at: time.now().unix()
|
||||
modified_at: time.now().unix()
|
||||
accessed_at: time.now().unix()
|
||||
}
|
||||
|
||||
result << MailFSEntry{
|
||||
path: '${mailbox}/id'
|
||||
metadata: id_metadata
|
||||
}
|
||||
|
||||
// Create subject directory
|
||||
subject_metadata := vfs.Metadata{
|
||||
id: u32('${mailbox}/subject'.bytes().bytestr().hash())
|
||||
name: 'subject'
|
||||
file_type: .directory
|
||||
created_at: time.now().unix()
|
||||
modified_at: time.now().unix()
|
||||
accessed_at: time.now().unix()
|
||||
}
|
||||
|
||||
result << MailFSEntry{
|
||||
path: '${mailbox}/subject'
|
||||
metadata: subject_metadata
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
fn (mut myvfs MailVFS) list_emails_by_type(mailbox string, list_type string, emails []mail.Email) ![]vfs.FSEntry {
|
||||
mut result := []vfs.FSEntry{}
|
||||
|
||||
for email in emails {
|
||||
if email.mailbox.split('/')[0] != mailbox {
|
||||
continue
|
||||
}
|
||||
|
||||
if list_type == 'id' {
|
||||
filename := '${email.id}.json'
|
||||
metadata := vfs.Metadata{
|
||||
id: email.id
|
||||
name: filename
|
||||
file_type: .file
|
||||
size: u64(json.encode(email).len)
|
||||
created_at: email.internal_date
|
||||
modified_at: email.internal_date
|
||||
accessed_at: time.now().unix()
|
||||
}
|
||||
|
||||
result << MailFSEntry{
|
||||
path: '${mailbox}/id/${filename}'
|
||||
metadata: metadata
|
||||
email: email
|
||||
}
|
||||
} else if list_type == 'subject' {
|
||||
if envelope := email.envelope {
|
||||
filename := texttools.name_fix(envelope.subject) + '.json'
|
||||
metadata := vfs.Metadata{
|
||||
id: email.id
|
||||
name: filename
|
||||
file_type: .file
|
||||
size: u64(json.encode(email).len)
|
||||
created_at: email.internal_date
|
||||
modified_at: email.internal_date
|
||||
accessed_at: time.now().unix()
|
||||
}
|
||||
|
||||
result << MailFSEntry{
|
||||
path: '${mailbox}/subject/${filename}'
|
||||
metadata: metadata
|
||||
email: email
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
133
lib/vfs/vfs_mail/vfs_implementation_test.v
Normal file
133
lib/vfs/vfs_mail/vfs_implementation_test.v
Normal file
@@ -0,0 +1,133 @@
|
||||
module vfs_mail
|
||||
|
||||
import freeflowuniverse.herolib.vfs
|
||||
import freeflowuniverse.herolib.circles.models
|
||||
import freeflowuniverse.herolib.circles.models.mail
|
||||
import freeflowuniverse.herolib.circles.dbs.core
|
||||
import json
|
||||
import time
|
||||
|
||||
fn test_mail_vfs() {
|
||||
// Create a session state
|
||||
mut session_state := models.new_session(name: 'test')!
|
||||
|
||||
// Create a mail database
|
||||
mut mail_db := core.new_maildb(session_state)!
|
||||
|
||||
// Create some test emails
|
||||
mut email1 := mail.Email{
|
||||
id: 1
|
||||
uid: 101
|
||||
seq_num: 1
|
||||
mailbox: 'Draft/important'
|
||||
message: 'This is a test email 1'
|
||||
internal_date: time.now().unix()
|
||||
envelope: mail.Envelope{
|
||||
subject: 'Test Email 1'
|
||||
from: ['sender1@example.com']
|
||||
to: ['recipient1@example.com']
|
||||
date: time.now().unix()
|
||||
}
|
||||
}
|
||||
|
||||
mut email2 := mail.Email{
|
||||
id: 2
|
||||
uid: 102
|
||||
seq_num: 2
|
||||
mailbox: 'Draft/normal'
|
||||
message: 'This is a test email 2'
|
||||
internal_date: time.now().unix()
|
||||
envelope: mail.Envelope{
|
||||
subject: 'Test Email 2'
|
||||
from: ['sender2@example.com']
|
||||
to: ['recipient2@example.com']
|
||||
date: time.now().unix()
|
||||
}
|
||||
}
|
||||
|
||||
mut email3 := mail.Email{
|
||||
id: 3
|
||||
uid: 103
|
||||
seq_num: 3
|
||||
mailbox: 'Inbox'
|
||||
message: 'This is a test email 3'
|
||||
internal_date: time.now().unix()
|
||||
envelope: mail.Envelope{
|
||||
subject: 'Test Email 3'
|
||||
from: ['sender3@example.com']
|
||||
to: ['recipient3@example.com']
|
||||
date: time.now().unix()
|
||||
}
|
||||
}
|
||||
|
||||
// Add emails to the database
|
||||
mail_db.set(email1) or { panic(err) }
|
||||
mail_db.set(email2) or { panic(err) }
|
||||
mail_db.set(email3) or { panic(err) }
|
||||
|
||||
// Create a mail VFS
|
||||
mut mail_vfs := new(&mail_db) or { panic(err) }
|
||||
|
||||
// Test root directory
|
||||
root := mail_vfs.root_get() or { panic(err) }
|
||||
assert root.is_dir()
|
||||
|
||||
// Test listing mailboxes
|
||||
mailboxes := mail_vfs.dir_list('') or { panic(err) }
|
||||
assert mailboxes.len == 2 // Draft and Inbox
|
||||
|
||||
// Find the Draft mailbox
|
||||
mut draft_found := false
|
||||
mut inbox_found := false
|
||||
for entry in mailboxes {
|
||||
if entry.get_metadata().name == 'Draft' {
|
||||
draft_found = true
|
||||
}
|
||||
if entry.get_metadata().name == 'Inbox' {
|
||||
inbox_found = true
|
||||
}
|
||||
}
|
||||
assert draft_found
|
||||
assert inbox_found
|
||||
|
||||
// Test listing mailbox subdirectories
|
||||
draft_subdirs := mail_vfs.dir_list('Draft') or { panic(err) }
|
||||
assert draft_subdirs.len == 2 // id and subject
|
||||
|
||||
// Test listing emails by ID
|
||||
draft_emails_by_id := mail_vfs.dir_list('Draft/id') or { panic(err) }
|
||||
assert draft_emails_by_id.len == 2 // email1 and email2
|
||||
|
||||
// Test listing emails by subject
|
||||
draft_emails_by_subject := mail_vfs.dir_list('Draft/subject') or { panic(err) }
|
||||
assert draft_emails_by_subject.len == 2 // email1 and email2
|
||||
|
||||
// Test getting an email by ID
|
||||
email1_by_id := mail_vfs.get('Draft/id/1.json') or { panic(err) }
|
||||
assert email1_by_id.is_file()
|
||||
|
||||
// Test reading an email by ID
|
||||
email1_content := mail_vfs.file_read('Draft/id/1.json') or { panic(err) }
|
||||
email1_json := json.decode(mail.Email, email1_content.bytestr()) or { panic(err) }
|
||||
assert email1_json.id == 1
|
||||
assert email1_json.mailbox == 'Draft/important'
|
||||
|
||||
// // Test getting an email by subject
|
||||
// email1_by_subject := mail_vfs.get('Draft/subject/Test Email 1.json') or { panic(err) }
|
||||
// assert email1_by_subject.is_file()
|
||||
|
||||
// // Test reading an email by subject
|
||||
// email1_content_by_subject := mail_vfs.file_read('Draft/subject/Test Email 1.json') or { panic(err) }
|
||||
// email1_json_by_subject := json.decode(mail.Email, email1_content_by_subject.bytestr()) or { panic(err) }
|
||||
// assert email1_json_by_subject.id == 1
|
||||
// assert email1_json_by_subject.mailbox == 'Draft/important'
|
||||
|
||||
// Test exists function
|
||||
assert mail_vfs.exists('Draft')
|
||||
assert mail_vfs.exists('Draft/id')
|
||||
assert mail_vfs.exists('Draft/id/1.json')
|
||||
// assert mail_vfs.exists('Draft/subject/Test Email 1.json')
|
||||
assert !mail_vfs.exists('NonExistentMailbox')
|
||||
|
||||
println('All mail VFS tests passed!')
|
||||
}
|
||||
17
lib/vfs/vfs_mail/vfs_mail.v
Normal file
17
lib/vfs/vfs_mail/vfs_mail.v
Normal file
@@ -0,0 +1,17 @@
|
||||
module vfs_mail
|
||||
|
||||
import freeflowuniverse.herolib.vfs
|
||||
import freeflowuniverse.herolib.circles.dbs.core
|
||||
|
||||
// MailVFS implements the VFS interface for mail objects
|
||||
pub struct MailVFS {
|
||||
pub mut:
|
||||
mail_db &core.MailDB
|
||||
}
|
||||
|
||||
// new_mail_vfs creates a new mail VFS
|
||||
pub fn new_mail_vfs(mail_db &core.MailDB) !vfs.VFSImplementation {
|
||||
return &MailVFS{
|
||||
mail_db: mail_db
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user