sqlite_vtable_opendal/vtab/
mod.rs1use crate::types::{columns, FileMetadata};
7use rusqlite::{
8 ffi,
9 vtab::{self, CreateVTab, IndexInfo, VTab, VTabCursor, VTabKind},
10 Result,
11};
12use std::os::raw::c_int;
13
14#[repr(C)]
19pub struct OpenDalTable {
20 base: ffi::sqlite3_vtab,
22}
23
24#[repr(C)]
28pub struct OpenDalCursor {
29 base: ffi::sqlite3_vtab_cursor,
31 files: Vec<FileMetadata>,
33 current_row: usize,
35}
36
37impl OpenDalCursor {
38 fn new() -> Self {
40 Self {
41 base: ffi::sqlite3_vtab_cursor::default(),
42 files: Vec::new(),
43 current_row: 0,
44 }
45 }
46}
47
48unsafe impl VTabCursor for OpenDalCursor {
49 fn filter(
54 &mut self,
55 _idx_num: c_int,
56 _idx_str: Option<&str>,
57 _args: &vtab::Values<'_>,
58 ) -> Result<()> {
59 self.files = Vec::new();
62 self.current_row = 0;
63 Ok(())
64 }
65
66 fn next(&mut self) -> Result<()> {
68 self.current_row += 1;
69 Ok(())
70 }
71
72 fn eof(&self) -> bool {
74 self.current_row >= self.files.len()
75 }
76
77 fn column(&self, ctx: &mut vtab::Context, col_index: c_int) -> Result<()> {
79 if self.current_row >= self.files.len() {
80 return Ok(());
81 }
82
83 let file = &self.files[self.current_row];
84
85 match col_index {
86 columns::PATH => ctx.set_result(&file.path),
87 columns::SIZE => ctx.set_result(&(file.size as i64)),
88 columns::LAST_MODIFIED => ctx.set_result(&file.last_modified),
89 columns::ETAG => ctx.set_result(&file.etag),
90 columns::IS_DIR => ctx.set_result(&file.is_dir),
91 columns::CONTENT_TYPE => ctx.set_result(&file.content_type),
92 columns::NAME => ctx.set_result(&file.name),
93 columns::CONTENT => {
94 if let Some(ref content) = file.content {
95 ctx.set_result(&content.as_slice())
96 } else {
97 ctx.set_result::<Option<&[u8]>>(&None)
98 }
99 }
100 _ => Ok(()),
101 }
102 }
103
104 fn rowid(&self) -> Result<i64> {
106 Ok(self.current_row as i64)
107 }
108}
109
110impl CreateVTab<'_> for OpenDalTable {
111 const KIND: VTabKind = VTabKind::EponymousOnly;
113}
114
115unsafe impl VTab<'_> for OpenDalTable {
116 type Aux = ();
117 type Cursor = OpenDalCursor;
118
119 fn connect(
123 _db: &mut vtab::VTabConnection,
124 _aux: Option<&Self::Aux>,
125 _args: &[&[u8]],
126 ) -> Result<(String, Self)> {
127 let schema = "
131 CREATE TABLE x(
132 path TEXT,
133 size INTEGER,
134 last_modified TEXT,
135 etag TEXT,
136 is_dir INTEGER,
137 content_type TEXT,
138 name TEXT,
139 content BLOB
140 )
141 ";
142
143 Ok((
144 schema.to_owned(),
145 OpenDalTable {
146 base: ffi::sqlite3_vtab::default(),
147 },
148 ))
149 }
150
151 fn best_index(&self, info: &mut IndexInfo) -> Result<()> {
155 info.set_estimated_cost(1000.0);
158 Ok(())
159 }
160
161 fn open(&mut self) -> Result<Self::Cursor> {
163 Ok(OpenDalCursor::new())
164 }
165}
166
167#[cfg(test)]
168mod tests {
169 use super::*;
170
171 #[test]
172 fn test_cursor_creation() {
173 let cursor = OpenDalCursor::new();
174 assert_eq!(cursor.current_row, 0);
175 assert!(cursor.files.is_empty());
176 assert!(cursor.eof());
177 }
178
179 #[test]
180 fn test_cursor_navigation() {
181 let mut cursor = OpenDalCursor::new();
182 assert!(cursor.eof());
183
184 cursor.next().unwrap();
185 assert_eq!(cursor.current_row, 1);
186 }
187}