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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
extern crate libsqlite3_sys as ffi;

type Connection = SqliteCon;

mod rows;
mod stmt;
mod traits;

pub use rows::{Row, Rows};
use std::os::raw::c_int;
pub use stmt::Stmt;

#[derive(Clone, Debug)]
pub enum Value {
    Int(i64),
    Uint(u64),
    Float(f64),
    String(String),
    Bytes(Vec<u8>),
    Null,
}

pub enum SqliteTypes {
    Int,
    FLoat,
    Blob,
    Null,
    Text,
}
#[derive(Debug)]
pub enum Error {
    SqliteError(String),
    Unknown(String),
    BindError(String),
    PrepareErr(String),
    IndexOutOfBounds(String),
    ConnectionErr(String),
    Empty,
}

type Lesult<T> = Result<T, Error>;

impl SqliteTypes {
    pub fn new(code: c_int) -> Lesult<Self> {
        match code {
            ffi::SQLITE_INTEGER => Ok(SqliteTypes::Int),
            ffi::SQLITE_FLOAT => Ok(SqliteTypes::FLoat),
            ffi::SQLITE_BLOB => Ok(SqliteTypes::Blob),
            ffi::SQLITE_NULL => Ok(SqliteTypes::Null),
            ffi::SQLITE_TEXT => Ok(SqliteTypes::Text),
            _ => Err(Error::Unknown("Cannot SQLITE type".to_string())),
        }
    }
}

#[derive(Debug)]
pub struct QueryResult<'a> {
    con: &'a SqliteCon,
    affected_rows: Option<usize>,
    last_insert_id: Option<u64>,
    rows: Rows<'a>,
}

#[derive(Debug)]
pub struct SqliteCon {
    pub con: *mut ffi::sqlite3,
}

impl SqliteCon {
    pub fn new(path: &str) -> Lesult<Self> {
        let mut a = unsafe { std::mem::uninitialized() };
        let cstr = std::ffi::CString::new(path)
            .map_err(|_| Error::ConnectionErr("Failed to parse path".to_string()))?;
        let res = unsafe {
            ffi::sqlite3_open_v2(
                cstr.as_ptr(),
                &mut a,
                ffi::SQLITE_OPEN_READWRITE | ffi::SQLITE_OPEN_CREATE,
                std::ptr::null(),
            )
        };

        if res != ffi::SQLITE_OK {
            return Err(Error::ConnectionErr("Failed to open database".to_string()));
        }

        Ok(Self { con: a })
    }

    pub fn get_last_error(&self, _err_code: i32) -> String {
        let err_cstr = unsafe { ffi::sqlite3_errmsg(self.con) };

        if err_cstr.is_null() {
            String::new()
        } else {
            let cstring = unsafe { std::ffi::CString::from_raw(err_cstr as *mut i8) };
            let clone = cstring.clone().into_string().unwrap();
            std::mem::forget(cstring);
            clone
        }
    }

    pub fn affected_rows(&self) -> usize {
        let ar = unsafe { ffi::sqlite3_changes(self.con) } as usize;
        ar
    }

    pub fn last_insert_id(&self) -> u64 {
        let lid = unsafe { ffi::sqlite3_last_insert_rowid(self.con) } as u64;
        lid
    }

    pub fn is_null(&self) -> bool {
        self.con.is_null()
    }
}

impl Drop for SqliteCon {
    fn drop(&mut self) {
        unsafe {
            ffi::sqlite3_close(self.con);
        }
    }
}

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn it_works() {
        assert_eq!(2 + 2, 4);
    }

    #[test]
    fn data_test() {
        let con = SqliteCon::new("test_db").expect("Failed to create Sqlite db");
        let sql = "DROP TABLE IF EXISTS user";
        let mut stmt = Stmt::init(&con);
        stmt.prepare(sql).unwrap();
        stmt.execute().unwrap();
        let sql = "CREATE TABLE user (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL DEFAULT '')";
        let mut stmt = Stmt::init(&con);
        stmt.prepare(sql).unwrap();
        stmt.execute().unwrap();
        let sql = "INSERT INTO user (name) VALUES (?), (?), (?)";
        let mut stmt = Stmt::init(&con);
        stmt.prepare(sql).unwrap();
        let params = vec!["name1", "name2", "name3"];
        stmt.bind_values(&params).unwrap();
        stmt.execute().unwrap();
        let sql = "SELECT * FROM user";
        stmt.prepare(sql).unwrap();
        //stmt.bind_values(vec![]).unwrap();
        let rows = stmt.get_rows();
        let rows_iter = rows.iter();
        let row_map = rows_iter.map(|row| row.get::<String, &str>("name").unwrap());
        let names: Vec<String> = row_map.collect();
        println!("names is {:?}", names);
        assert_eq!(
            names,
            vec![
                "name1".to_string(),
                "name2".to_string(),
                "name3".to_string(),
            ]
        );
    }
}