Add an error management in db module

This commit is contained in:
Greg Burri 2020-06-24 23:52:41 +02:00
parent e2e54b8f43
commit a080d19cb9
6 changed files with 387 additions and 280 deletions

1
.gitignore vendored
View file

@ -5,3 +5,4 @@ backend/data/recipes.sqlite-journal
/deploy-to-pi.ps1 /deploy-to-pi.ps1
style.css.map style.css.map
backend/static/style.css backend/static/style.css
backend/file.db

555
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -3,6 +3,6 @@
* Implement the model as relational with SQLite. * Implement the model as relational with SQLite.
* Create and update functions. * Create and update functions.
* Define the UI (mockups). * Define the UI (mockups).
* Use Bulma to implement the UI in HTML5+CSS3. * Two CSS: one for desktop and one for mobile
* Define the logic behind each page and action. * Define the logic behind each page and action.
* Try using WASM for all the client logic * Try using WASM for all the client logic

View file

@ -10,10 +10,10 @@ actix-rt = "1"
actix-files = "0.2" actix-files = "0.2"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
listenfd = "0.3" # To watch file modifications and automatically launch a build process (only used in dev/debug). listenfd = "0.3" # To watch file modifications and automatically launch a build process (only used in dev/debug).
ron = "0.5" # Rust object notation, to load configuration files. ron = "0.6" # Rust object notation, to load configuration files.
itertools = "0.9" itertools = "0.9"
r2d2_sqlite = "0.15" # Connection pool with rusqlite (SQLite access). r2d2_sqlite = "0.16" # Connection pool with rusqlite (SQLite access).
r2d2 = "0.8" r2d2 = "0.8"
futures = "0.3" # Needed by askam with the feature 'with-actix-web'. futures = "0.3" # Needed by askam with the feature 'with-actix-web'.
@ -21,11 +21,10 @@ futures = "0.3" # Needed by askam with the feature 'with-actix-web'.
common = { path = "../common" } common = { path = "../common" }
[dependencies.rusqlite] [dependencies.rusqlite]
version = "0.22" version = "0.23"
features = ["bundled"] features = ["bundled"]
# Template system. # Template system.
[dependencies.askama] [dependencies.askama]
version = "0.9" version = "0.9"
features = ["with-actix-web"] features = ["with-actix-web"]

View file

@ -11,6 +11,12 @@ use std::fs;
use r2d2_sqlite::SqliteConnectionManager; use r2d2_sqlite::SqliteConnectionManager;
use r2d2::Pool; use r2d2::Pool;
#[derive(Debug)]
pub enum DbError {
SqliteError(rusqlite::Error),
R2d2Error(r2d2::Error),
UnsupportedVersion(i32),
}
use super::consts; use super::consts;
@ -26,8 +32,20 @@ pub struct Recipe {
pub id: i32, pub id: i32,
} }
impl std::convert::From<rusqlite::Error> for DbError {
fn from(error: rusqlite::Error) -> Self {
DbError::SqliteError(error)
}
}
impl std::convert::From<r2d2::Error> for DbError {
fn from(error: r2d2::Error) -> Self {
DbError::R2d2Error(error)
}
}
impl Connection { impl Connection {
pub fn new() -> Connection { pub fn new() -> Result<Connection, DbError> {
let data_dir = Path::new(consts::DB_DIRECTORY); let data_dir = Path::new(consts::DB_DIRECTORY);
@ -39,59 +57,83 @@ impl Connection {
let pool = r2d2::Pool::new(manager).unwrap(); let pool = r2d2::Pool::new(manager).unwrap();
let connection = Connection { pool }; let connection = Connection { pool };
connection.create_or_update(); connection.create_or_update()?;
connection Ok(connection)
} }
fn create_or_update(self: &Self) { /*
* 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 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 * 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(); // let mut stmt = connection.sqlite_con.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='versions'").unwrap();
// Check the Database version. // Check the Database version.
let con = self.pool.get().unwrap(); let mut con = self.pool.get()?;
let tx = con.transaction()?;
let version = { let mut version = {
match match tx.query_row(
con.query_row(
"SELECT [name] FROM [sqlite_master] WHERE [type] = 'table' AND [name] = 'Version'", "SELECT [name] FROM [sqlite_master] WHERE [type] = 'table' AND [name] = 'Version'",
rusqlite::NO_PARAMS, rusqlite::NO_PARAMS,
|row| row.get::<usize, String>(0) |row| row.get::<usize, String>(0)
) { ) {
Ok(_) => tx.query_row("SELECT [version] FROM [Version]", rusqlite::NO_PARAMS, |row| row.get(0)).unwrap_or_default(),
Ok(_) => con.query_row("SELECT [version] FROM [Version]", rusqlite::NO_PARAMS, |row| row.get(0)).unwrap_or_default(),
Err(_) => 0 Err(_) => 0
} }
}; };
while Connection::update_to_next_version(version, &tx)? {
version += 1;
}
tx.commit()?;
Ok(())
}
fn update_to_next_version(version: i32, tx: &rusqlite::Transaction) -> Result<bool, DbError> {
match version { match version {
0 => { 0 => {
println!("Update to version 1..."); println!("Update to version 1...");
con.execute(
// Initial structure.
tx.execute_batch(
" "
CREATE TABLE [Version] ( CREATE TABLE [Version] (
[id] INTEGER PRIMARY KEY, [id] INTEGER PRIMARY KEY,
[version] INTEGER NOT NULL, [version] INTEGER NOT NULL UNIQUE,
[datetime] INTEGER DATETIME [datetime] INTEGER DATETIME
)
",
rusqlite::NO_PARAMS
); );
con.execute(
"
CREATE TABLE [Recipe] ( CREATE TABLE [Recipe] (
[id] INTEGER PRIMARY KEY, [id] INTEGER PRIMARY KEY,
[title] INTEGER NOT NULL, [title] INTEGER NOT NULL,
[description] INTEGER DATETIME [description] INTEGER DATETIME
) );
"
)?;
/*
tx.execute(
"
INSERT INTO Version
", ",
rusqlite::NO_PARAMS rusqlite::NO_PARAMS
); );*/
()
Ok(true)
} }
// Current version.
1 =>
Ok(false),
v => v =>
panic!("Unsupported database version: {}", v) Err(DbError::UnsupportedVersion(v)),
}; }
} }
pub fn get_all_recipes() { pub fn get_all_recipes() {

View file

@ -39,7 +39,6 @@ async fn home_page(req: HttpRequest) -> impl Responder {
#[get("/recipe/view/{id}")] #[get("/recipe/view/{id}")]
async fn view_page(req: HttpRequest, path: web::Path<(i32,)>) -> impl Responder { async fn view_page(req: HttpRequest, path: web::Path<(i32,)>) -> impl Responder {
panic!("ERROR");
ViewRecipeTemplate { recipes: vec![ db::Recipe { title: String::from("Saumon en croûte feuilletée"), id: 1 }, db::Recipe { title: String::from("Croissant au jambon"), id: 2 } ], current_recipe: db::Recipe { title: String::from("Saumon en croûte feuilletée"), id: 1 } } ViewRecipeTemplate { recipes: vec![ db::Recipe { title: String::from("Saumon en croûte feuilletée"), id: 1 }, db::Recipe { title: String::from("Croissant au jambon"), id: 2 } ], current_recipe: db::Recipe { title: String::from("Saumon en croûte feuilletée"), id: 1 } }
} }
@ -108,7 +107,10 @@ fn process_args() -> bool {
print_usage(); print_usage();
return true return true
} else if args.iter().any(|arg| arg == "--test") { } else if args.iter().any(|arg| arg == "--test") {
let db_connection = db::Connection::new(); match db::Connection::new() {
Ok(_) => (),
Err(error) => println!("Error: {:?}", error)
}
return true return true
} }