use crate::types::{columns, FileMetadata};
use rusqlite::{
ffi,
vtab::{self, CreateVTab, IndexInfo, VTab, VTabCursor, VTabKind},
Result,
};
use std::os::raw::c_int;
#[repr(C)]
pub struct OpenDalTable {
base: ffi::sqlite3_vtab,
}
#[repr(C)]
pub struct OpenDalCursor {
base: ffi::sqlite3_vtab_cursor,
files: Vec<FileMetadata>,
current_row: usize,
}
impl OpenDalCursor {
fn new() -> Self {
Self {
base: ffi::sqlite3_vtab_cursor::default(),
files: Vec::new(),
current_row: 0,
}
}
}
unsafe impl VTabCursor for OpenDalCursor {
fn filter(
&mut self,
_idx_num: c_int,
_idx_str: Option<&str>,
_args: &vtab::Filters<'_>,
) -> Result<()> {
self.files = Vec::new();
self.current_row = 0;
Ok(())
}
fn next(&mut self) -> Result<()> {
self.current_row += 1;
Ok(())
}
fn eof(&self) -> bool {
self.current_row >= self.files.len()
}
fn column(&self, ctx: &mut vtab::Context, col_index: c_int) -> Result<()> {
if self.current_row >= self.files.len() {
return Ok(());
}
let file = &self.files[self.current_row];
match col_index {
columns::PATH => ctx.set_result(&file.path),
columns::SIZE => ctx.set_result(&(file.size as i64)),
columns::LAST_MODIFIED => ctx.set_result(&file.last_modified),
columns::ETAG => ctx.set_result(&file.etag),
columns::IS_DIR => ctx.set_result(&file.is_dir),
columns::CONTENT_TYPE => ctx.set_result(&file.content_type),
columns::NAME => ctx.set_result(&file.name),
columns::CONTENT => {
if let Some(ref content) = file.content {
ctx.set_result(&content.as_slice())
} else {
ctx.set_result::<Option<&[u8]>>(&None)
}
}
_ => Ok(()),
}
}
fn rowid(&self) -> Result<i64> {
Ok(self.current_row as i64)
}
}
impl CreateVTab<'_> for OpenDalTable {
const KIND: VTabKind = VTabKind::EponymousOnly;
}
unsafe impl VTab<'_> for OpenDalTable {
type Aux = ();
type Cursor = OpenDalCursor;
fn connect(
_db: &mut vtab::VTabConnection,
_aux: Option<&Self::Aux>,
_args: &[&[u8]],
) -> Result<(String, Self)> {
let schema = "
CREATE TABLE x(
path TEXT,
size INTEGER,
last_modified TEXT,
etag TEXT,
is_dir INTEGER,
content_type TEXT,
name TEXT,
content BLOB
)
";
Ok((
schema.to_owned(),
OpenDalTable {
base: ffi::sqlite3_vtab::default(),
},
))
}
fn best_index(&self, info: &mut IndexInfo) -> Result<()> {
info.set_estimated_cost(1000.0);
Ok(())
}
fn open(&mut self) -> Result<Self::Cursor> {
Ok(OpenDalCursor::new())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cursor_creation() {
let cursor = OpenDalCursor::new();
assert_eq!(cursor.current_row, 0);
assert!(cursor.files.is_empty());
assert!(cursor.eof());
}
#[test]
fn test_cursor_navigation() {
let mut cursor = OpenDalCursor::new();
assert!(cursor.eof());
cursor.next().unwrap();
assert_eq!(cursor.current_row, 1);
}
}