2bf383b4e395c49550c58689a316a6cfe79adbbe
1 use std
::{fs
::{self, File
}, path
::Path
, io
::Read
};
3 use itertools
::Itertools
;
4 //use rusqlite::types::ToSql;
5 //use rusqlite::{Connection, Result, NO_PARAMS};
7 use r2d2_sqlite
::SqliteConnectionManager
;
12 const CURRENT_DB_VERSION
: u32 = 1;
16 SqliteError(rusqlite
::Error
),
17 R2d2Error(r2d2
::Error
),
18 UnsupportedVersion(u32),
22 impl From
<rusqlite
::Error
> for DBError
{
23 fn from(error
: rusqlite
::Error
) -> Self {
24 DBError
::SqliteError(error
)
28 impl From
<r2d2
::Error
> for DBError
{
29 fn from(error
: r2d2
::Error
) -> Self {
30 DBError
::R2d2Error(error
)
34 type Result
<T
> = std
::result
::Result
<T
, DBError
>;
37 pub struct Connection
{
38 //con: rusqlite::Connection
39 pool
: Pool
<SqliteConnectionManager
>
43 pub fn new() -> Result
<Connection
> {
45 let data_dir
= Path
::new(consts
::DB_DIRECTORY
);
47 if !data_dir
.exists() {
48 fs
::DirBuilder
::new().create(data_dir
).unwrap();
51 let manager
= SqliteConnectionManager
::file(consts
::DB_FILENAME
);
52 let pool
= r2d2
::Pool
::new(manager
).unwrap();
54 let connection
= Connection
{ pool
};
55 connection
.create_or_update()?
;
60 * Called after the connection has been established for creating or updating the database.
61 * The 'Version' table tracks the current state of the database.
63 fn create_or_update(&self) -> Result
<()> {
64 // Check the Database version.
65 let mut con
= self.pool
.get()?
;
66 let tx
= con
.transaction()?
;
68 // Version 0 corresponds to an empty database.
71 "SELECT [name] FROM [sqlite_master] WHERE [type] = 'table' AND [name] = 'Version'",
73 |row
| row
.get
::<usize, String
>(0)
75 Ok(_
) => tx
.query_row("SELECT [version] FROM [Version] ORDER BY [id] DESC", [], |row
| row
.get(0)).unwrap_or_default(),
80 while Connection
::update_to_next_version(version
, &tx
)?
{
89 fn update_to_next_version(current_version
: u32, tx
: &rusqlite
::Transaction
) -> Result
<bool
> {
90 let next_version
= current_version
+ 1;
92 if next_version
<= CURRENT_DB_VERSION
{
93 println!("Update to version {}...", next_version
);
96 fn update_version(to_version
: u32, tx
: &rusqlite
::Transaction
) -> Result
<()> {
97 tx
.execute("INSERT INTO [Version] ([version], [datetime]) VALUES (?1, datetime('now'))", [to_version
]).map(|_
| ()).map_err(DBError
::from
)
100 fn ok(updated
: bool
) -> Result
<bool
> {
102 println!("Version updated");
109 tx
.execute_batch(&load_sql_file(next_version
)?
)?
;
110 update_version(next_version
, tx
)?
;
115 // Version 1 doesn't exist yet.
120 Err(DBError
::UnsupportedVersion(v
)),
124 pub fn get_all_recipe_titles(&self) -> Result
<Vec
<(i32, String
)>> {
125 let con
= self.pool
.get()?
;
126 let mut stmt
= con
.prepare("SELECT [id], [title] FROM [Recipe] ORDER BY [title]")?
;
128 stmt
.query_map([], |row
| {
129 Ok((row
.get(0)?
, row
.get(1)?
))
130 })?
.map(|r
| r
.unwrap()).collect_vec(); // TODO: remove unwrap.
134 pub fn get_all_recipes(&self) -> Result
<Vec
<model
::Recipe
>> {
135 let con
= self.pool
.get()?
;
136 let mut stmt
= con
.prepare("SELECT [id], [title] FROM [Recipe] ORDER BY [title]")?
;
138 stmt
.query_map([], |row
| {
139 Ok(model
::Recipe
::new(row
.get(0)?
, row
.get(1)?
))
140 })?
.map(|r
| r
.unwrap()).collect_vec(); // TODO: remove unwrap.
144 pub fn get_recipe(&self, id
: i32) -> Result
<model
::Recipe
> {
145 let con
= self.pool
.get()?
;
146 con
.query_row("SELECT [id], [title] FROM [Recipe] WHERE [id] = ?1", [id
], |row
| {
147 Ok(model
::Recipe
::new(row
.get(0)?
, row
.get(1)?
))
148 }).map_err(DBError
::from
)
152 fn load_sql_file(version
: u32) -> Result
<String
> {
153 let sql_file
= consts
::SQL_FILENAME
.replace("{VERSION}", &version
.to_string());
154 let mut file
= File
::open(&sql_file
).map_err(|err
| DBError
::Other(format!("Cannot open SQL file ({}): {}", &sql_file
, err
.to_string())))?
;
155 let mut sql
= String
::new();
156 file
.read_to_string(&mut sql
).map_err(|err
| DBError
::Other(format!("Cannot read SQL file ({}) : {}", &sql_file
, err
.to_string())))?
;