forensic_rs/traits/
sql.rs

1use crate::{prelude::ForensicResult, err::ForensicError};
2
3use super::vfs::VirtualFile;
4
5pub trait SqlDb {
6    fn list_tables(&self) -> ForensicResult<Vec<String>>;
7    fn prepare<'a>(&'a self, statement : &'a str) -> ForensicResult<Box<dyn SqlStatement + 'a>>;
8    /// Mounts a SQL reader from a sqlite file
9    fn from_file(&self, file: Box<dyn VirtualFile>) -> ForensicResult<Box<dyn SqlDb>>;
10}
11
12/// It allows decoupling the SQL database access library from the analysis library.
13pub trait SqlStatement {
14    /// Return the number of columns.
15    fn column_count(&self) -> usize;
16    /// Return the name of a column. The first column has index 0.
17    fn column_name(&self, i: usize) -> Option<&str>;
18    /// Return column names.
19    fn column_names(&self) -> Vec<&str>;
20    /// Return the type of a column. The first column has index 0.
21    fn column_type(&self, i: usize) -> ColumnType;
22    /// Advance to the next state until there no more data is available (return=Ok(false)).
23    fn next(&mut self) -> ForensicResult<bool>;
24    /// Read a value from a column. The first column has index 0.
25    fn read(&self, i: usize) -> ForensicResult<ColumnValue>;
26}
27
28pub trait SqlValueInto: Sized {
29    fn into(value: &ColumnValue) -> ForensicResult<Self>;
30    fn into_owned(value: ColumnValue) -> ForensicResult<Self>;
31}
32
33pub enum ColumnType {
34    Binary,
35    Float,
36    Integer,
37    String,
38    Null,
39}
40
41pub enum ColumnValue {
42    Binary(Vec<u8>),
43    Float(f64),
44    Integer(i64),
45    String(String),
46    Null,
47}
48
49impl TryInto<String> for ColumnValue {
50    type Error = ForensicError;
51
52    fn try_into(self) -> Result<String, Self::Error> {
53        Ok(match self {
54            ColumnValue::String(v) => v.clone(),
55            ColumnValue::Binary(v) => format!("{:?}",v),
56            ColumnValue::Float(v) => format!("{:?}",v),
57            ColumnValue::Integer(v) => format!("{:?}",v),
58            ColumnValue::Null => String::new()
59        })
60    }
61}
62
63impl TryInto<i64> for ColumnValue {
64    type Error = ForensicError;
65
66    fn try_into(self) -> Result<i64, Self::Error> {
67        match self {
68            ColumnValue::Integer(v) => Ok(v),
69            _ => Err(ForensicError::CastError)
70        }
71    }
72}
73
74impl TryInto<usize> for ColumnValue {
75    type Error = ForensicError;
76
77    fn try_into(self) -> Result<usize, Self::Error> {
78        match self {
79            ColumnValue::Integer(v) => Ok(v as usize),
80            _ => Err(ForensicError::CastError)
81        }
82    }
83}
84
85impl TryInto<f64> for ColumnValue {
86    type Error = ForensicError;
87
88    fn try_into(self) -> Result<f64, Self::Error> {
89        match self {
90            ColumnValue::Float(v) => Ok(v),
91            _ => Err(ForensicError::CastError)
92        }
93    }
94}
95
96impl TryInto<Vec<u8>> for ColumnValue {
97    type Error = ForensicError;
98
99    fn try_into(self) -> Result<Vec<u8>, Self::Error> {
100        match self {
101            ColumnValue::Binary(v) => Ok(v),
102            _ => Err(ForensicError::CastError)
103        }
104    }
105}
106
107impl Into<ColumnValue> for f32 {
108    fn into(self) -> ColumnValue {
109        ColumnValue::Float(self as f64)
110    }
111}
112
113impl Into<ColumnValue> for f64 {
114    fn into(self) -> ColumnValue {
115        ColumnValue::Float(self)
116    }
117}
118impl Into<ColumnValue> for u64 {
119    fn into(self) -> ColumnValue {
120        ColumnValue::Integer(self as i64)
121    }
122}
123impl Into<ColumnValue> for i64 {
124    fn into(self) -> ColumnValue {
125        ColumnValue::Integer(self as i64)
126    }
127}
128impl Into<ColumnValue> for u32 {
129    fn into(self) -> ColumnValue {
130        ColumnValue::Integer(self as i64)
131    }
132}
133impl Into<ColumnValue> for i32 {
134    fn into(self) -> ColumnValue {
135        ColumnValue::Integer(self as i64)
136    }
137}
138impl Into<ColumnValue> for usize {
139    fn into(self) -> ColumnValue {
140        ColumnValue::Integer(self as i64)
141    }
142}
143impl Into<ColumnValue> for Vec<u8> {
144    fn into(self) -> ColumnValue {
145        ColumnValue::Binary(self)
146    }
147}
148impl Into<ColumnValue> for &Vec<u8> {
149    fn into(self) -> ColumnValue {
150        ColumnValue::Binary(self.clone())
151    }
152}
153impl Into<ColumnValue> for () {
154    fn into(self) -> ColumnValue {
155        ColumnValue::Null
156    }
157}
158impl Into<ColumnValue> for &[u8] {
159    fn into(self) -> ColumnValue {
160        let mut vc = Vec::with_capacity(self.len());
161        for v in self {
162            vc.push(*v);
163        }
164        ColumnValue::Binary(vc)
165    }
166}
167impl Into<ColumnValue> for &str {
168    fn into(self) -> ColumnValue {
169        ColumnValue::String(self.to_string())
170    }
171}
172impl Into<ColumnValue> for &String {
173    fn into(self) -> ColumnValue {
174        ColumnValue::String(self.to_string())
175    }
176}
177impl Into<ColumnValue> for String {
178    fn into(self) -> ColumnValue {
179        ColumnValue::String(self)
180    }
181}
182
183#[cfg(test)]
184mod sql_tests {
185    extern crate sqlite;
186
187    use crate::prelude::{ForensicResult, ForensicError};
188    use self::sqlite::{Connection, Statement};
189    use super::{SqlStatement, ColumnValue, SqlDb};
190
191    struct SqliteWDB {
192        conn : Connection
193    }
194
195    impl SqliteWDB{
196        pub fn new(conn : Connection) -> SqliteWDB {
197            SqliteWDB {
198                conn
199            }
200        }
201    }
202
203    impl SqlDb for SqliteWDB {
204        fn prepare<'a>(&'a self, statement : &'a str) -> ForensicResult<Box<dyn SqlStatement +'a>> {
205            Ok(Box::new(SqliteStatement::new( &self.conn, statement)?))
206        }
207
208        fn from_file(&self, _file: Box<dyn crate::traits::vfs::VirtualFile>) -> ForensicResult<Box<dyn SqlDb>> {
209            match sqlite::open(":memory:") {
210                Ok(v) => Ok(Box::new(Self::new(v))),
211                Err(e) => Err(ForensicError::Other(e.to_string()))
212            }
213        }
214
215        fn list_tables(&self) -> ForensicResult<Vec<String>> {
216            let mut ret = Vec::with_capacity(32);
217            let mut sts = self.prepare(r#"SELECT 
218            name
219        FROM 
220            sqlite_schema
221        WHERE 
222            type ='table' AND 
223            name NOT LIKE 'sqlite_%';"#)?;
224            loop {
225                if !sts.next()? {
226                    break;
227                }
228                let name : String = sts.read(0)?.try_into()?;
229                ret.push(name);
230            }
231            Ok(ret)
232        }
233    }
234
235    pub struct SqliteStatement<'conn> {
236        stmt: Statement<'conn>
237    }
238    impl<'conn> SqliteStatement<'conn>{
239        pub fn new(conn : &'conn Connection, statement : &str) -> ForensicResult<SqliteStatement<'conn>>{
240            Ok(Self { stmt : match conn.prepare(statement) {
241                    Ok(st) => st,
242                    Err(e) => return Err(ForensicError::Other(e.to_string()))
243                }
244            })
245        }
246    }
247
248    impl<'conn> SqlStatement for SqliteStatement<'conn>{
249        fn column_count(&self) -> usize {
250            self.stmt.column_count()
251        }
252
253        fn column_name(&self, i: usize) -> Option<&str> {
254            Some(self.stmt.column_name(i))
255        }
256
257        fn column_names(&self) -> Vec<&str> {
258            self.stmt.column_names()
259        }
260
261        fn column_type(&self, i: usize) -> super::ColumnType {
262            match self.stmt.column_type(i) {
263                sqlite::Type::Binary => super::ColumnType::Binary,
264                sqlite::Type::Float => super::ColumnType::Float,
265                sqlite::Type::Integer => super::ColumnType::Integer,
266                sqlite::Type::String => super::ColumnType::String,
267                sqlite::Type::Null => super::ColumnType::Null,
268            }
269        }
270
271        fn next(&mut self) -> ForensicResult<bool> {
272            match self.stmt.next() {
273                Ok(v) => Ok(match v {
274                    sqlite::State::Row => true,
275                    sqlite::State::Done => false,
276                }),
277                Err(e) => Err(ForensicError::Other(e.to_string())),
278            }
279        }
280
281        fn read(&self, i: usize) -> ForensicResult<ColumnValue> {
282            match self.stmt.column_type(i) {
283                sqlite::Type::Binary => match self.stmt.read(i) {
284                    Ok(v) => Ok(ColumnValue::Binary(v)),
285                    Err(e) => Err(ForensicError::Other(e.to_string())),
286                },
287                sqlite::Type::Float => match self.stmt.read(i) {
288                    Ok(v) => Ok(ColumnValue::Float(v)),
289                    Err(e) => Err(ForensicError::Other(e.to_string())),
290                },
291                sqlite::Type::Integer => match self.stmt.read(i) {
292                    Ok(v) => Ok(ColumnValue::Integer(v)),
293                    Err(e) => Err(ForensicError::Other(e.to_string())),
294                },
295                sqlite::Type::String => match self.stmt.read(i) {
296                    Ok(v) => Ok(ColumnValue::String(v)),
297                    Err(e) => Err(ForensicError::Other(e.to_string())),
298                },
299                sqlite::Type::Null => Ok(ColumnValue::Null),
300            }
301        }
302
303    }
304
305    fn prepare_db() -> Connection {
306        let connection = sqlite::open(":memory:").unwrap();
307        connection
308            .execute(
309                "
310            CREATE TABLE users (name TEXT, age INTEGER);
311            INSERT INTO users VALUES ('Alice', 42);
312            INSERT INTO users VALUES ('Bob', 69);
313            ",
314            )
315            .unwrap();
316        connection
317    }
318    fn prepare_wrapper(connection :Connection) -> SqliteWDB{
319        SqliteWDB::new(connection)
320    }
321
322
323    #[test]
324    fn test_sqlite_wrapper() {
325        let conn = prepare_db();
326        let w_conn = prepare_wrapper(conn);
327        let mut statement = w_conn.prepare("SELECT name, age FROM users;").unwrap();
328        test_database_content(statement.as_mut()).expect("Should not return error");
329        
330    }
331
332    fn test_database_content<'a>(statement : &mut dyn SqlStatement) -> ForensicResult<()> {
333        assert!(statement.next()?);
334        let name : String = statement.read(0)?.try_into()?;
335        let age : usize = statement.read(1)?.try_into()?;
336        assert_eq!("Alice", name);
337        assert_eq!(42, age);
338        assert!(statement.next()?);
339        let name : String = statement.read(0)?.try_into()?;
340        let age : usize = statement.read(1)?.try_into()?;
341        assert_eq!("Bob", name);
342        assert_eq!(69, age);
343        assert!(!statement.next()?);
344        Ok(())
345    }
346}