1use sqlite_loadable::prelude::*;
5use sqlite_loadable::{
6 api, define_table_function,
7 table::{BestIndexError, ConstraintOperator, IndexInfo, VTab, VTabArguments, VTabCursor},
8 Result,
9};
10
11use std::mem;
12
13static CREATE_SQL: &str = "CREATE TABLE x(value, input hidden)";
14enum Columns {
15 Value,
16 Input,
17}
18fn column(index: i32) -> Option<Columns> {
19 match index {
20 0 => Some(Columns::Value),
21 1 => Some(Columns::Input),
22 _ => None,
23 }
24}
25
26#[repr(C)]
27pub struct CharactersTable {
28 base: sqlite3_vtab,
30}
31
32impl<'vtab> VTab<'vtab> for CharactersTable {
33 type Aux = ();
34 type Cursor = CharactersCursor;
35
36 fn connect(
37 _db: *mut sqlite3,
38 _aux: Option<&Self::Aux>,
39 _args: VTabArguments,
40 ) -> Result<(String, CharactersTable)> {
41 let vtab = CharactersTable { base: unsafe { mem::zeroed() } };
42 Ok((CREATE_SQL.to_owned(), vtab))
44 }
45 fn destroy(&self) -> Result<()> {
46 Ok(())
47 }
48
49 fn best_index(&self, mut info: IndexInfo) -> core::result::Result<(), BestIndexError> {
50 let mut has_input = false;
51 for mut constraint in info.constraints() {
52 match column(constraint.column_idx()) {
53 Some(Columns::Input) => {
54 if constraint.usable() && constraint.op() == Some(ConstraintOperator::EQ) {
55 constraint.set_omit(true);
56 constraint.set_argv_index(1);
57 has_input = true;
58 } else {
59 return Err(BestIndexError::Constraint);
60 }
61 }
62 _ => todo!(),
63 }
64 }
65 if !has_input {
66 return Err(BestIndexError::Error);
67 }
68 info.set_estimated_cost(100000.0);
69 info.set_estimated_rows(100000);
70 info.set_idxnum(1);
71
72 Ok(())
73 }
74
75 fn open(&mut self) -> Result<CharactersCursor> {
76 Ok(CharactersCursor::new())
77 }
78}
79
80#[repr(C)]
81pub struct CharactersCursor {
82 base: sqlite3_vtab_cursor,
84 input: Option<String>,
85 characters: Option<Vec<char>>,
86 idx: usize,
87}
88impl CharactersCursor {
89 fn new() -> CharactersCursor {
90 CharactersCursor {
91 base: unsafe { mem::zeroed() },
92 input: None,
93 characters: None,
94 idx: 0,
95 }
96 }
97}
98
99impl VTabCursor for CharactersCursor {
100 fn filter(
101 &mut self,
102 _idx_num: i32,
103 _idx_str: Option<&str>,
104 values: &[*mut sqlite3_value],
105 ) -> Result<()> {
106 let input =
107 api::value_text(values.get(0).expect("1st input constraint is required"))?.to_owned();
108 self.characters = Some(input.chars().collect());
109 self.input = Some(input);
110 self.idx = 0;
111 Ok(())
112 }
113
114 fn next(&mut self) -> Result<()> {
115 self.idx += 1;
116 Ok(())
117 }
118
119 fn eof(&self) -> bool {
120 match &self.characters {
121 Some(chars) => chars.get(self.idx).is_none(),
122 None => true,
123 }
124 }
125
126 fn column(&self, context: *mut sqlite3_context, i: i32) -> Result<()> {
127 match column(i) {
128 Some(Columns::Value) => {
129 api::result_text(
130 context,
131 self.characters
132 .as_ref()
133 .unwrap()
134 .get(self.idx)
135 .unwrap()
136 .to_string()
137 .as_str(),
138 )?;
139 }
140 Some(Columns::Input) => {
141 api::result_text(context, self.input.as_ref().unwrap())?;
142 }
143 _ => (),
144 }
145 Ok(())
146 }
147
148 fn rowid(&self) -> Result<i64> {
149 Ok(self.idx as i64)
150 }
151}
152
153#[sqlite_entrypoint]
154pub fn sqlite3_characters_init(db: *mut sqlite3) -> Result<()> {
155 define_table_function::<CharactersTable>(db, "characters", None)?;
156 Ok(())
157}