-
Notifications
You must be signed in to change notification settings - Fork 1
/
mod.rs
151 lines (133 loc) · 4.58 KB
/
mod.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#![allow(non_snake_case)]
use rusqlite::ffi as sqlite3;
use std::cell::RefCell;
use std::ffi::CString;
use std::mem::{self, MaybeUninit};
use std::os::raw;
use std::ptr::NonNull;
use std::rc::Rc;
mod file;
mod system;
pub use file::VirtualFile as File;
pub use system::VirtualFilesystem as System;
/// Represents the access level of a file.
// FIXME: Turn into a bitwise flag.
pub enum AccessFlag {
#[allow(unused)]
Exists = sqlite3::SQLITE_ACCESS_EXISTS as _,
#[allow(unused)]
ReadOnly = sqlite3::SQLITE_ACCESS_READ as _,
#[allow(unused)]
ReadWrite = sqlite3::SQLITE_ACCESS_READWRITE as _,
}
// FIXME: Turn into a bitwise flag.
pub enum LockFlag {
None = sqlite3::SQLITE_LOCK_NONE as _,
Shared = sqlite3::SQLITE_LOCK_SHARED as _,
Reserved = sqlite3::SQLITE_LOCK_RESERVED as _,
Pending = sqlite3::SQLITE_LOCK_PENDING as _,
Exclusive = sqlite3::SQLITE_LOCK_EXCLUSIVE as _,
}
#[repr(C)]
#[derive(Clone)]
pub struct Instance {
ptr: sqlite3::sqlite3_vfs,
fs: Rc<RefCell<dyn System>>,
vfs_name: CString,
}
impl Instance {
pub fn new(
vfs_name: impl ToString,
filesystem: Rc<RefCell<dyn System>>,
) -> anyhow::Result<Rc<RefCell<Self>>> {
let vfs: sqlite3::sqlite3_vfs = unsafe { MaybeUninit::uninit().assume_init() };
Ok(Rc::new(RefCell::new(Self {
ptr: vfs,
fs: Rc::clone(&filesystem),
vfs_name: CString::new(vfs_name.to_string().into_bytes())?,
})))
}
/// The name of the VFS.
pub fn vfs_name(&self) -> Option<String> {
CString::into_string(self.vfs_name.clone()).ok()
}
pub fn filesystem(&self) -> Rc<RefCell<dyn System>> {
if self.fs.as_ptr().is_null() {
panic!("Somehow, we lost the pointer.");
} else {
Rc::clone(&self.fs)
}
}
fn into_raw(instance_rc: Rc<RefCell<Self>>) -> *mut raw::c_void {
Box::into_raw(Box::new(Rc::clone(&instance_rc))) as *mut raw::c_void
}
pub fn register(instance_rc: Rc<RefCell<Self>>, make_default: bool) -> anyhow::Result<()> {
if !instance_rc.borrow().registered() {
{
let mut instance_mut = instance_rc.borrow_mut();
system::bind(&mut instance_mut.ptr);
instance_mut.ptr.zName = instance_mut.vfs_name.as_ptr() as _;
instance_mut.ptr.pAppData = Self::into_raw(Rc::clone(&instance_rc));
}
log::info!(
"Attempting to register VFS for {:?}",
instance_rc.borrow().vfs_name
);
// FIXME: Look into leaning on rusqlite to handle error reporting from SQLite.
let register_result = unsafe {
sqlite3::sqlite3_vfs_register(
&mut instance_rc.borrow_mut().ptr,
make_default as raw::c_int,
)
};
if register_result == sqlite3::SQLITE_OK as _ {
log::info!(
"Registered {:?} into the SQLite VFS index.",
instance_rc.borrow().vfs_name
);
mem::forget(instance_rc);
Ok(())
} else {
log::error!(
"Failed to register {:?} into the SQLite VFS index (code: {}).",
instance_rc.borrow().vfs_name,
register_result,
);
Err(anyhow::anyhow!("Failed to register VFS"))
}
} else {
Ok(())
}
}
pub fn unregister(instance: &mut Self) -> anyhow::Result<()> {
let unregister_result = unsafe { sqlite3::sqlite3_vfs_unregister(&mut instance.ptr) };
if unregister_result == sqlite3::SQLITE_OK as _ {
log::info!(
"Unregistered {:?} into the SQLite VFS index.",
instance.vfs_name
);
Ok(())
} else {
log::error!(
"Failed to unregister {:?} into the SQLite VFS index (code: {}).",
instance.vfs_name,
unregister_result,
);
Err(anyhow::anyhow!("Failed to unregister VFS"))
}
}
/// Checks to see if the `VirtualFilesystem` held by this instance has been registered.
pub fn registered(&self) -> bool {
NonNull::new(unsafe { sqlite3::sqlite3_vfs_find(self.vfs_name.as_ptr() as _) }).is_some()
}
}
impl Drop for Instance {
fn drop(&mut self) {
if self.registered() {
assert!(Instance::unregister(self).is_ok())
}
mem::drop(self.ptr);
}
}
#[cfg(test)]
mod test;