tiempo-rs/src/database.rs

144 lines
4.3 KiB
Rust
Raw Normal View History

2021-06-21 11:12:30 -05:00
use std::path::Path;
2021-06-18 11:27:19 -05:00
use rusqlite::{Connection, ToSql};
use chrono::{DateTime, Utc};
2021-06-21 11:12:30 -05:00
2021-06-30 18:51:02 -05:00
use crate::error::{Error, Result};
2021-06-21 11:12:30 -05:00
use crate::models::{Entry, Meta};
2021-06-18 11:27:19 -05:00
pub enum DBVersion {
Timetrap,
Version(u16),
}
2021-06-18 11:27:19 -05:00
pub trait Database {
2021-06-21 11:12:30 -05:00
/// This is used to create tables and insert rows
2021-06-30 18:51:02 -05:00
fn execute(&mut self, query: &str, params: &[&dyn ToSql]) -> Result<()>;
2021-06-21 11:12:30 -05:00
/// And this is used to retrieve data
2021-06-30 18:51:02 -05:00
fn entry_query(&self, query: &str, params: &[&dyn ToSql]) -> Result<Vec<Entry>>;
fn meta_query(&self, query: &str, params: &[&dyn ToSql]) -> Result<Vec<Meta>>;
// ----------
// Migrations
// ----------
fn init(&mut self) -> Result<()> {
self.execute("CREATE TABLE `entries`
(
`id` integer NOT NULL PRIMARY KEY AUTOINCREMENT,\
`note` varchar(255),
`start` timestamp,
`end` timestamp,
`sheet` varchar(255)
)
", &[])?;
self.execute("CREATE TABLE `meta`
(
`id` integer NOT NULL PRIMARY KEY AUTOINCREMENT,
`key` varchar(255),
`value` varchar(255)
)
", &[])?;
2021-06-21 11:12:30 -05:00
2021-06-30 18:51:02 -05:00
Ok(())
}
2021-06-21 17:38:51 -05:00
2021-06-30 18:51:02 -05:00
// -------------
// Entry queries
// -------------
fn entries_by_sheet(&self, sheet: &str) -> Result<Vec<Entry>> {
2021-06-21 21:35:05 -05:00
self.entry_query("select * from entries where sheet=?1 order by start asc", &[&sheet])
2021-06-21 11:12:30 -05:00
}
2021-06-30 18:51:02 -05:00
fn entries_all_visible(&self) -> Result<Vec<Entry>> {
2021-06-21 21:35:05 -05:00
self.entry_query("select * from entries where sheet not like '!_%' escape \"!\" order by sheet asc, start asc", &[])
}
2021-06-30 18:51:02 -05:00
fn entries_full(&self) -> Result<Vec<Entry>> {
2021-06-21 21:35:05 -05:00
self.entry_query("select * from entries order by sheet asc, start asc", &[])
}
2021-06-30 18:51:02 -05:00
fn entry_insert(&mut self, start: DateTime<Utc>, end: Option<DateTime<Utc>>, note: Option<String>, sheet: String) -> Result<()> {
self.execute("insert into entries (start, end, note, sheet) values (?1, ?2, ?3, ?4)", &[
&start, &end, &note, &sheet,
])
2021-06-21 11:12:30 -05:00
}
2021-06-21 17:38:51 -05:00
// Meta queries
2021-06-30 18:51:02 -05:00
fn current_sheet(&self) -> Result<Option<String>> {
let results = self.meta_query("select * from meta where key='current_sheet'", &[])?;
2021-06-21 17:38:51 -05:00
Ok(results.into_iter().next().map(|m| m.value))
}
2021-06-30 18:51:02 -05:00
fn version(&self) -> Result<DBVersion> {
let results = self.meta_query("select * from meta where key='database_version'", &[])?;
if let Some(v) = results.into_iter().next().map(|m| m.value) {
2021-06-30 18:51:02 -05:00
Ok(DBVersion::Version(v.parse().map_err(|_| {
Error::CorruptedData(format!(
"Found value '{}' for key 'database_version' in meta table, which is not a valid integer",
v
))
})?))
} else {
Ok(DBVersion::Timetrap)
}
}
2021-06-18 11:27:19 -05:00
}
pub struct SqliteDatabase {
2021-06-21 11:12:30 -05:00
connection: Connection,
2021-06-18 11:27:19 -05:00
}
impl SqliteDatabase {
2021-06-30 18:51:02 -05:00
pub fn from_memory() -> Result<impl Database> {
2021-06-18 11:27:19 -05:00
Ok(SqliteDatabase {
2021-06-21 11:12:30 -05:00
connection: Connection::open_in_memory()?,
2021-06-18 11:27:19 -05:00
})
}
2021-06-30 18:51:02 -05:00
pub fn from_path<P: AsRef<Path>>(path: P) -> Result<impl Database> {
2021-06-18 11:27:19 -05:00
Ok(SqliteDatabase {
2021-06-21 11:12:30 -05:00
connection: Connection::open(path)?,
2021-06-18 11:27:19 -05:00
})
}
}
impl Database for SqliteDatabase {
2021-06-30 18:51:02 -05:00
fn execute(&mut self, query: &str, params: &[&dyn ToSql]) -> Result<()> {
2021-06-21 11:12:30 -05:00
self.connection.execute(query, params)?;
Ok(())
}
2021-06-30 18:51:02 -05:00
fn entry_query(&self, query: &str, params: &[&dyn ToSql]) -> Result<Vec<Entry>> {
2021-06-21 11:12:30 -05:00
let mut stmt = self.connection.prepare(query)?;
2021-06-21 17:38:51 -05:00
let results = stmt.query_map(params, |row| {
2021-06-21 11:12:30 -05:00
let x = Ok(Entry {
id: row.get("id")?,
note: row.get("note")?,
start: row.get("start")?,
end: row.get("end")?,
sheet: row.get("sheet")?,
});
x})?.filter_map(|r| r.ok()).collect();
Ok(results)
}
2021-06-30 18:51:02 -05:00
fn meta_query(&self, query: &str, params: &[&dyn ToSql]) -> Result<Vec<Meta>> {
2021-06-21 11:12:30 -05:00
let mut stmt = self.connection.prepare(query)?;
2021-06-21 17:38:51 -05:00
let results = stmt.query_map(params, |row| Ok(Meta {
2021-06-21 11:12:30 -05:00
id: row.get("id")?,
key: row.get("key")?,
value: row.get("value")?,
}))?.filter_map(|r| r.ok()).collect();
Ok(results)
2021-06-18 11:27:19 -05:00
}
}