Add some data access methods to Connection

This commit is contained in:
Greg Burri 2022-11-19 00:16:07 +01:00
parent 4fbc599d07
commit cdb883c3c4
6 changed files with 180 additions and 114 deletions

View file

@ -1,14 +1,14 @@
use crate::consts::SQL_FILENAME;
use super::consts;
use std::{fs::{self, File}, path::Path, io::Read};
use itertools::Itertools;
//use rusqlite::types::ToSql;
//use rusqlite::{Connection, Result, NO_PARAMS};
use r2d2::Pool;
use r2d2_sqlite::SqliteConnectionManager;
use crate::consts;
use crate::model;
const CURRENT_DB_VERSION: u32 = 1;
#[derive(Debug)]
@ -19,30 +19,28 @@ pub enum DBError {
Other(String),
}
pub struct Connection {
//con: rusqlite::Connection
pool: Pool<SqliteConnectionManager>
}
pub struct Recipe {
pub title: String,
pub id: i32,
}
impl std::convert::From<rusqlite::Error> for DBError {
impl From<rusqlite::Error> for DBError {
fn from(error: rusqlite::Error) -> Self {
DBError::SqliteError(error)
}
}
impl std::convert::From<r2d2::Error> for DBError {
impl From<r2d2::Error> for DBError {
fn from(error: r2d2::Error) -> Self {
DBError::R2d2Error(error)
}
}
type Result<T> = std::result::Result<T, DBError>;
#[derive(Clone)]
pub struct Connection {
//con: rusqlite::Connection
pool: Pool<SqliteConnectionManager>
}
impl Connection {
pub fn new() -> Result<Connection, DBError> {
pub fn new() -> Result<Connection> {
let data_dir = Path::new(consts::DB_DIRECTORY);
@ -62,11 +60,7 @@ impl Connection {
* Called after the connection has been established for creating or updating the database.
* The 'Version' table tracks the current state of the database.
*/
fn create_or_update(self: &Self) -> Result<(), DBError> {
// let connection = Connection::new();
// let mut stmt = connection.sqlite_con.prepare("SELECT * FROM versions ORDER BY date").unwrap();
// let mut stmt = connection.sqlite_con.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='versions'").unwrap();
fn create_or_update(&self) -> Result<()> {
// Check the Database version.
let mut con = self.pool.get()?;
let tx = con.transaction()?;
@ -78,7 +72,7 @@ impl Connection {
[],
|row| row.get::<usize, String>(0)
) {
Ok(_) => tx.query_row("SELECT [version] FROM [Version]", [], |row| row.get(0)).unwrap_or_default(),
Ok(_) => tx.query_row("SELECT [version] FROM [Version] ORDER BY [id] DESC", [], |row| row.get(0)).unwrap_or_default(),
Err(_) => 0
}
};
@ -92,14 +86,18 @@ impl Connection {
Ok(())
}
fn update_to_next_version(current_version: u32, tx: &rusqlite::Transaction) -> Result<bool, DBError> {
fn update_to_next_version(current_version: u32, tx: &rusqlite::Transaction) -> Result<bool> {
let next_version = current_version + 1;
if next_version <= CURRENT_DB_VERSION {
println!("Update to version {}...", next_version);
}
fn ok(updated: bool) -> Result<bool, DBError> {
fn update_version(to_version: u32, tx: &rusqlite::Transaction) -> Result<()> {
tx.execute("INSERT INTO [Version] ([version], [datetime]) VALUES (?1, datetime('now'))", [to_version]).map(|_| ()).map_err(DBError::from)
}
fn ok(updated: bool) -> Result<bool> {
if updated {
println!("Version updated");
}
@ -109,6 +107,7 @@ impl Connection {
match next_version {
1 => {
tx.execute_batch(&load_sql_file(next_version)?)?;
update_version(next_version, tx)?;
ok(true)
}
@ -122,13 +121,36 @@ impl Connection {
}
}
pub fn get_all_recipes() {
pub fn get_all_recipe_titles(&self) -> Result<Vec<(i32, String)>> {
let con = self.pool.get()?;
let mut stmt = con.prepare("SELECT [id], [title] FROM [Recipe] ORDER BY [title]")?;
let titles =
stmt.query_map([], |row| {
Ok((row.get(0)?, row.get(1)?))
})?.map(|r| r.unwrap()).collect_vec(); // TODO: remove unwrap.
Ok(titles)
}
pub fn get_all_recipes(&self) -> Result<Vec<model::Recipe>> {
let con = self.pool.get()?;
let mut stmt = con.prepare("SELECT [id], [title] FROM [Recipe] ORDER BY [title]")?;
let recipes =
stmt.query_map([], |row| {
Ok(model::Recipe::new(row.get(0)?, row.get(1)?))
})?.map(|r| r.unwrap()).collect_vec(); // TODO: remove unwrap.
Ok(recipes)
}
pub fn get_recipe(&self, id: i32) -> Result<model::Recipe> {
let con = self.pool.get()?;
con.query_row("SELECT [id], [title] FROM [Recipe] WHERE [id] = ?1", [id], |row| {
Ok(model::Recipe::new(row.get(0)?, row.get(1)?))
}).map_err(DBError::from)
}
}
fn load_sql_file(version: u32) -> Result<String, DBError> {
let sql_file = SQL_FILENAME.replace("{VERSION}", &version.to_string());
fn load_sql_file(version: u32) -> Result<String> {
let sql_file = consts::SQL_FILENAME.replace("{VERSION}", &version.to_string());
let mut file = File::open(&sql_file).map_err(|err| DBError::Other(format!("Cannot open SQL file ({}): {}", &sql_file, err.to_string())))?;
let mut sql = String::new();
file.read_to_string(&mut sql).map_err(|err| DBError::Other(format!("Cannot read SQL file ({}) : {}", &sql_file, err.to_string())))?;