2021-06-21 11:12:30 -05:00
|
|
|
use std::path::Path;
|
2021-06-18 11:27:19 -05:00
|
|
|
|
2021-06-24 00:07:52 -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
|
|
|
|
2021-06-29 18:20:51 -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
|
|
|
|
// ----------
|
2021-07-17 22:04:54 -05:00
|
|
|
/// Create a database in the new database format. Actually the same format
|
|
|
|
/// just it has an entry in the meta table that indicates the database
|
|
|
|
/// version.
|
2021-06-30 18:51:02 -05:00
|
|
|
fn init(&mut self) -> Result<()> {
|
2021-07-17 22:04:54 -05:00
|
|
|
self.init_old()?;
|
|
|
|
self.execute("INSERT INTO meta (key, value) VALUES ('database_version', 1)", &[])?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Creates the tables for the old database format
|
|
|
|
fn init_old(&mut self) -> Result<()> {
|
2021-06-30 18:51:02 -05:00
|
|
|
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-07-23 20:37:21 -05:00
|
|
|
self.execute("INSERT INTO meta (key, value) VALUES ('current_sheet', 'default')", &[])?;
|
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
|
|
|
|
// -------------
|
2021-06-30 19:20:10 -05:00
|
|
|
fn entries_by_sheet(&self, sheet: &str, start: Option<DateTime<Utc>>, end: Option<DateTime<Utc>>) -> Result<Vec<Entry>> {
|
|
|
|
match (start, end) {
|
|
|
|
(Some(start), Some(end)) => {
|
|
|
|
self.entry_query(
|
2021-07-07 11:54:18 -05:00
|
|
|
"select * from entries where sheet=?1 and start >= ?2 and start <= ?3 order by start asc",
|
2021-06-30 19:20:10 -05:00
|
|
|
&[&sheet, &start, &end]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
(Some(start), None) => {
|
|
|
|
self.entry_query(
|
|
|
|
"select * from entries where sheet=?1 and start >= ?2 order by start asc",
|
|
|
|
&[&sheet, &start]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
(None, Some(end)) => {
|
|
|
|
self.entry_query(
|
2021-07-07 11:54:18 -05:00
|
|
|
"select * from entries where sheet=?1 and start <= ?2 order by start asc",
|
2021-06-30 19:20:10 -05:00
|
|
|
&[&sheet, &end]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
(None, None) => {
|
|
|
|
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 19:20:10 -05:00
|
|
|
fn entries_all_visible(&self, start: Option<DateTime<Utc>>, end: Option<DateTime<Utc>>) -> Result<Vec<Entry>> {
|
|
|
|
match (start, end) {
|
|
|
|
(Some(start), Some(end)) => {
|
|
|
|
self.entry_query(
|
2021-07-16 17:52:33 -05:00
|
|
|
"select * from entries where sheet not like '!_%' escape \"!\" and start >= ?1 and start <= ?2 order by sheet asc, start asc",
|
2021-06-30 19:20:10 -05:00
|
|
|
&[&start, &end]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
(Some(start), None) => {
|
|
|
|
self.entry_query(
|
2021-07-16 17:52:33 -05:00
|
|
|
"select * from entries where sheet not like '!_%' escape \"!\" and start >= ?1 order by sheet asc, start asc",
|
2021-06-30 19:20:10 -05:00
|
|
|
&[&start]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
(None, Some(end)) => {
|
|
|
|
self.entry_query(
|
2021-07-16 17:52:33 -05:00
|
|
|
"select * from entries where sheet not like '!_%' escape \"!\" and start <= ?1 order by sheet asc, start asc",
|
2021-06-30 19:20:10 -05:00
|
|
|
&[&end]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
(None, None) => {
|
|
|
|
self.entry_query(
|
2021-07-16 17:52:33 -05:00
|
|
|
"select * from entries where sheet not like '!_%' escape \"!\" order by sheet asc, start asc",
|
2021-06-30 19:20:10 -05:00
|
|
|
&[]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
2021-06-21 19:54:10 -05:00
|
|
|
}
|
|
|
|
|
2021-06-30 19:20:10 -05:00
|
|
|
fn entries_full(&self, start: Option<DateTime<Utc>>, end: Option<DateTime<Utc>>) -> Result<Vec<Entry>> {
|
|
|
|
match (start, end) {
|
|
|
|
(Some(start), Some(end)) => {
|
|
|
|
self.entry_query(
|
2021-07-16 17:52:33 -05:00
|
|
|
"select * from entries where start >= ?1 and start <= ?2 order by sheet asc, start asc",
|
2021-06-30 19:20:10 -05:00
|
|
|
&[&start, &end]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
(Some(start), None) => {
|
|
|
|
self.entry_query(
|
2021-07-16 17:52:33 -05:00
|
|
|
"select * from entries where start >= ?1 order by sheet asc, start asc",
|
2021-06-30 19:20:10 -05:00
|
|
|
&[&start]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
(None, Some(end)) => {
|
|
|
|
self.entry_query(
|
2021-07-16 17:52:33 -05:00
|
|
|
"select * from entries where start <= ?1 order by sheet asc, start asc",
|
2021-06-30 19:20:10 -05:00
|
|
|
&[&end]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
(None, None) => {
|
|
|
|
self.entry_query(
|
2021-07-16 17:52:33 -05:00
|
|
|
"select * from entries order by sheet asc, start asc",
|
2021-06-30 19:20:10 -05:00
|
|
|
&[]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
2021-06-21 19:54:10 -05:00
|
|
|
}
|
|
|
|
|
2021-07-17 22:04:54 -05:00
|
|
|
fn entry_insert(&mut self, start: DateTime<Utc>, end: Option<DateTime<Utc>>, note: Option<String>, sheet: &str) -> Result<()> {
|
2021-06-30 18:51:02 -05:00
|
|
|
self.execute("insert into entries (start, end, note, sheet) values (?1, ?2, ?3, ?4)", &[
|
|
|
|
&start, &end, ¬e, &sheet,
|
|
|
|
])
|
2021-06-21 11:12:30 -05:00
|
|
|
}
|
2021-06-21 17:38:51 -05:00
|
|
|
|
2021-07-20 10:08:04 -05:00
|
|
|
fn entry_update(&mut self, id: u64, start: DateTime<Utc>, end: Option<DateTime<Utc>>, note: Option<String>, sheet: &str) -> Result<()> {
|
|
|
|
self.execute("update entries set start=?2, end=?3, note=?4, sheet=?5 where id=?1", &[
|
|
|
|
&id, &start, &end, ¬e, &sheet
|
|
|
|
])
|
|
|
|
}
|
|
|
|
|
2021-07-26 16:55:24 -05:00
|
|
|
fn entry_by_id(&self, id: u64) -> Result<Option<Entry>> {
|
|
|
|
Ok(self.entry_query("select * from entries where id=?1", &[&id])?.into_iter().next())
|
|
|
|
}
|
|
|
|
|
2021-07-20 10:08:04 -05:00
|
|
|
fn running_entry(&self, sheet: &str) -> Result<Option<Entry>> {
|
|
|
|
Ok(self.entry_query("select * from entries where end is null and sheet=?1", &[&sheet])?.into_iter().next())
|
2021-07-17 22:04:54 -05:00
|
|
|
}
|
|
|
|
|
2021-08-02 18:45:54 -05:00
|
|
|
fn running_entries(&self) -> Result<Vec<Entry>> {
|
2021-08-02 19:10:34 -05:00
|
|
|
self.entry_query("select * from entries where end is null order by sheet asc", &[])
|
2021-08-02 18:45:54 -05:00
|
|
|
}
|
|
|
|
|
2021-07-27 12:20:58 -05:00
|
|
|
fn last_checkout_of_sheet(&self, sheet: &str) -> Result<Option<Entry>> {
|
|
|
|
Ok(self.entry_query("select * from entries where end is not null and sheet=?1 order by end desc limit 1", &[&sheet])?.into_iter().next())
|
|
|
|
}
|
|
|
|
|
2021-07-30 17:55:19 -05:00
|
|
|
fn delete_entry_by_id(&mut self, id: u64) -> Result<()> {
|
2021-08-02 19:10:34 -05:00
|
|
|
self.execute("delete from entries where id=?1", &[&id])
|
2021-07-30 17:55:19 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn delete_entries_in_sheet(&mut self, sheet: &str) -> Result<()> {
|
|
|
|
self.execute("delete from entries where sheet=?1", &[&sheet])?;
|
|
|
|
|
|
|
|
if let Some(last) = self.last_sheet()? {
|
|
|
|
if last == sheet {
|
|
|
|
self.unset_last_sheet()?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
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>> {
|
2021-06-29 18:20:51 -05:00
|
|
|
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-29 18:20:51 -05:00
|
|
|
|
2021-07-15 13:11:55 -05:00
|
|
|
fn last_sheet(&self) -> Result<Option<String>> {
|
|
|
|
let results = self.meta_query("select * from meta where key='last_sheet'", &[])?;
|
|
|
|
|
|
|
|
Ok(results.into_iter().next().map(|m| m.value))
|
|
|
|
}
|
|
|
|
|
2021-07-15 11:48:49 -05:00
|
|
|
fn set_current_sheet(&mut self, sheet: &str) -> Result<()> {
|
2021-07-23 20:37:21 -05:00
|
|
|
self.execute("DELETE FROM meta WHERE key='current_sheet'", &[])?;
|
2021-07-15 11:48:49 -05:00
|
|
|
self.execute("INSERT INTO meta (key, value) VALUES ('current_sheet', ?1)", &[&sheet])?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-07-16 12:45:27 -05:00
|
|
|
fn set_last_sheet(&mut self, sheet: &str) -> Result<()> {
|
2021-07-23 20:37:21 -05:00
|
|
|
self.execute("DELETE FROM meta WHERE key='last_sheet'", &[])?;
|
2021-07-16 12:45:27 -05:00
|
|
|
self.execute("INSERT INTO meta (key, value) VALUES ('last_sheet', ?1)", &[&sheet])?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-07-30 17:55:19 -05:00
|
|
|
fn unset_last_sheet(&mut self) -> Result<()> {
|
2021-08-02 19:10:34 -05:00
|
|
|
self.execute("delete from meta where key='last_sheet'", &[])
|
2021-07-30 17:55:19 -05:00
|
|
|
}
|
|
|
|
|
2021-06-30 18:51:02 -05:00
|
|
|
fn version(&self) -> Result<DBVersion> {
|
2021-06-29 18:20:51 -05:00
|
|
|
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!(
|
2021-06-29 18:20:51 -05:00
|
|
|
"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
|
|
|
})
|
|
|
|
}
|
2021-07-03 18:17:26 -05:00
|
|
|
|
|
|
|
pub fn from_path_or_create<P: AsRef<Path>>(path: P) -> Result<impl Database> {
|
|
|
|
if path.as_ref().is_file() {
|
|
|
|
Self::from_path(path)
|
|
|
|
} else {
|
|
|
|
let mut db = Self::from_path(path)?;
|
|
|
|
|
|
|
|
db.init()?;
|
|
|
|
|
|
|
|
Ok(db)
|
|
|
|
}
|
|
|
|
}
|
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-07-13 17:54:20 -05:00
|
|
|
let entries = stmt.query_and_then(params, |row| {
|
2021-07-13 17:07:07 -05:00
|
|
|
let id: u64 = row.get("id")?;
|
|
|
|
let note = row.get("note")?;
|
|
|
|
let sheet = row.get("sheet")?;
|
|
|
|
|
|
|
|
let start = row.get("start").map_err(|_| {
|
|
|
|
Error::InvalidTimeInDatabase {
|
|
|
|
id: id.to_string(),
|
|
|
|
col: "start".into(),
|
|
|
|
}
|
|
|
|
})?;
|
2021-07-13 17:54:20 -05:00
|
|
|
let end = row.get("end").map_err(|_| {
|
2021-07-13 17:07:07 -05:00
|
|
|
Error::InvalidTimeInDatabase {
|
|
|
|
id: id.to_string(),
|
|
|
|
col: "start".into(),
|
|
|
|
}
|
|
|
|
})?;
|
|
|
|
|
2021-07-01 23:44:54 -05:00
|
|
|
Ok(Entry {
|
2021-07-13 17:07:07 -05:00
|
|
|
id, note, start, end, sheet,
|
2021-07-01 23:44:54 -05:00
|
|
|
})
|
2021-07-13 17:07:07 -05:00
|
|
|
})?.collect();
|
2021-06-21 11:12:30 -05:00
|
|
|
|
2021-07-13 17:54:20 -05:00
|
|
|
entries
|
2021-06-21 11:12:30 -05:00
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
}
|
2021-07-07 11:54:18 -05:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use chrono::TimeZone;
|
|
|
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_entries_by_sheet() {
|
|
|
|
let mut db = SqliteDatabase::from_memory().unwrap();
|
|
|
|
db.init().unwrap();
|
|
|
|
|
|
|
|
db.entry_insert(Utc.ymd(2021, 7, 7).and_hms(1, 0, 0), None, None, "XXX".into()).unwrap();
|
|
|
|
db.entry_insert(Utc.ymd(2021, 7, 7).and_hms(1, 0, 0), None, None, "OOO".into()).unwrap();
|
|
|
|
db.entry_insert(Utc.ymd(2021, 7, 7).and_hms(2, 0, 0), None, None, "XXX".into()).unwrap();
|
|
|
|
db.entry_insert(Utc.ymd(2021, 7, 7).and_hms(2, 0, 0), None, None, "OOO".into()).unwrap();
|
|
|
|
db.entry_insert(Utc.ymd(2021, 7, 7).and_hms(3, 0, 0), None, None, "XXX".into()).unwrap();
|
|
|
|
db.entry_insert(Utc.ymd(2021, 7, 7).and_hms(3, 0, 0), None, None, "OOO".into()).unwrap();
|
|
|
|
|
|
|
|
let start = Utc.ymd(2021, 7, 7).and_hms(1, 30, 0);
|
|
|
|
let end = Utc.ymd(2021, 7, 7).and_hms(2, 30, 0);
|
|
|
|
|
|
|
|
// filter by start and end
|
|
|
|
assert_eq!(
|
|
|
|
db.entries_by_sheet("XXX", Some(start), Some(end)).unwrap().into_iter().map(|e| e.start).collect::<Vec<_>>(),
|
|
|
|
vec![
|
|
|
|
Utc.ymd(2021, 7, 7).and_hms(2, 0, 0),
|
|
|
|
]
|
|
|
|
);
|
|
|
|
|
|
|
|
// filter only by start
|
|
|
|
assert_eq!(
|
|
|
|
db.entries_by_sheet("XXX", Some(start), None).unwrap().into_iter().map(|e| e.start).collect::<Vec<_>>(),
|
|
|
|
vec![
|
|
|
|
Utc.ymd(2021, 7, 7).and_hms(2, 0, 0),
|
|
|
|
Utc.ymd(2021, 7, 7).and_hms(3, 0, 0),
|
|
|
|
]
|
|
|
|
);
|
|
|
|
|
|
|
|
// filter only by end
|
|
|
|
assert_eq!(
|
|
|
|
db.entries_by_sheet("XXX", None, Some(end)).unwrap().into_iter().map(|e| e.start).collect::<Vec<_>>(),
|
|
|
|
vec![
|
|
|
|
Utc.ymd(2021, 7, 7).and_hms(1, 0, 0),
|
|
|
|
Utc.ymd(2021, 7, 7).and_hms(2, 0, 0),
|
|
|
|
]
|
|
|
|
);
|
|
|
|
|
|
|
|
// no filter
|
|
|
|
assert_eq!(
|
|
|
|
db.entries_by_sheet("XXX", None, None).unwrap().into_iter().map(|e| e.start).collect::<Vec<_>>(),
|
|
|
|
vec![
|
|
|
|
Utc.ymd(2021, 7, 7).and_hms(1, 0, 0),
|
|
|
|
Utc.ymd(2021, 7, 7).and_hms(2, 0, 0),
|
|
|
|
Utc.ymd(2021, 7, 7).and_hms(3, 0, 0),
|
|
|
|
]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_entries_all() {
|
|
|
|
let mut db = SqliteDatabase::from_memory().unwrap();
|
|
|
|
db.init().unwrap();
|
|
|
|
|
|
|
|
db.entry_insert(Utc.ymd(2021, 7, 7).and_hms(1, 0, 0), None, None, "XXX".into()).unwrap();
|
|
|
|
db.entry_insert(Utc.ymd(2021, 7, 7).and_hms(1, 0, 0), None, None, "_OO".into()).unwrap();
|
|
|
|
db.entry_insert(Utc.ymd(2021, 7, 7).and_hms(2, 0, 0), None, None, "XXX".into()).unwrap();
|
|
|
|
db.entry_insert(Utc.ymd(2021, 7, 7).and_hms(2, 0, 0), None, None, "_OO".into()).unwrap();
|
|
|
|
db.entry_insert(Utc.ymd(2021, 7, 7).and_hms(3, 0, 0), None, None, "XXX".into()).unwrap();
|
|
|
|
db.entry_insert(Utc.ymd(2021, 7, 7).and_hms(3, 0, 0), None, None, "_OO".into()).unwrap();
|
|
|
|
|
|
|
|
let start = Utc.ymd(2021, 7, 7).and_hms(1, 30, 0);
|
|
|
|
let end = Utc.ymd(2021, 7, 7).and_hms(2, 30, 0);
|
|
|
|
|
|
|
|
// filter by start and end
|
|
|
|
assert_eq!(
|
|
|
|
db.entries_all_visible(Some(start), Some(end)).unwrap().into_iter().map(|e| e.start).collect::<Vec<_>>(),
|
|
|
|
vec![
|
|
|
|
Utc.ymd(2021, 7, 7).and_hms(2, 0, 0),
|
|
|
|
]
|
|
|
|
);
|
|
|
|
|
|
|
|
// filter only by start
|
|
|
|
assert_eq!(
|
|
|
|
db.entries_all_visible(Some(start), None).unwrap().into_iter().map(|e| e.start).collect::<Vec<_>>(),
|
|
|
|
vec![
|
|
|
|
Utc.ymd(2021, 7, 7).and_hms(2, 0, 0),
|
|
|
|
Utc.ymd(2021, 7, 7).and_hms(3, 0, 0),
|
|
|
|
]
|
|
|
|
);
|
|
|
|
|
|
|
|
// filter only by end
|
|
|
|
assert_eq!(
|
|
|
|
db.entries_all_visible(None, Some(end)).unwrap().into_iter().map(|e| e.start).collect::<Vec<_>>(),
|
|
|
|
vec![
|
|
|
|
Utc.ymd(2021, 7, 7).and_hms(1, 0, 0),
|
|
|
|
Utc.ymd(2021, 7, 7).and_hms(2, 0, 0),
|
|
|
|
]
|
|
|
|
);
|
|
|
|
|
|
|
|
// no filter
|
|
|
|
assert_eq!(
|
|
|
|
db.entries_all_visible(None, None).unwrap().into_iter().map(|e| e.start).collect::<Vec<_>>(),
|
|
|
|
vec![
|
|
|
|
Utc.ymd(2021, 7, 7).and_hms(1, 0, 0),
|
|
|
|
Utc.ymd(2021, 7, 7).and_hms(2, 0, 0),
|
|
|
|
Utc.ymd(2021, 7, 7).and_hms(3, 0, 0),
|
|
|
|
]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_entries_full() {
|
|
|
|
let mut db = SqliteDatabase::from_memory().unwrap();
|
|
|
|
db.init().unwrap();
|
|
|
|
|
|
|
|
db.entry_insert(Utc.ymd(2021, 7, 7).and_hms(1, 0, 0), None, None, "XXX".into()).unwrap();
|
|
|
|
db.entry_insert(Utc.ymd(2021, 7, 7).and_hms(1, 0, 0), None, None, "_OO".into()).unwrap();
|
|
|
|
db.entry_insert(Utc.ymd(2021, 7, 7).and_hms(2, 0, 0), None, None, "XXX".into()).unwrap();
|
|
|
|
db.entry_insert(Utc.ymd(2021, 7, 7).and_hms(2, 0, 0), None, None, "_OO".into()).unwrap();
|
|
|
|
db.entry_insert(Utc.ymd(2021, 7, 7).and_hms(3, 0, 0), None, None, "XXX".into()).unwrap();
|
|
|
|
db.entry_insert(Utc.ymd(2021, 7, 7).and_hms(3, 0, 0), None, None, "_OO".into()).unwrap();
|
|
|
|
|
|
|
|
let start = Utc.ymd(2021, 7, 7).and_hms(1, 30, 0);
|
|
|
|
let end = Utc.ymd(2021, 7, 7).and_hms(2, 30, 0);
|
|
|
|
|
|
|
|
// filter by start and end
|
|
|
|
assert_eq!(
|
|
|
|
db.entries_full(Some(start), Some(end)).unwrap().into_iter().map(|e| e.start).collect::<Vec<_>>(),
|
|
|
|
vec![
|
|
|
|
Utc.ymd(2021, 7, 7).and_hms(2, 0, 0),
|
|
|
|
Utc.ymd(2021, 7, 7).and_hms(2, 0, 0),
|
|
|
|
]
|
|
|
|
);
|
|
|
|
|
|
|
|
// filter only by start
|
|
|
|
assert_eq!(
|
|
|
|
db.entries_full(Some(start), None).unwrap().into_iter().map(|e| e.start).collect::<Vec<_>>(),
|
|
|
|
vec![
|
|
|
|
Utc.ymd(2021, 7, 7).and_hms(2, 0, 0),
|
|
|
|
Utc.ymd(2021, 7, 7).and_hms(3, 0, 0),
|
2021-07-16 18:07:11 -05:00
|
|
|
Utc.ymd(2021, 7, 7).and_hms(2, 0, 0),
|
2021-07-07 11:54:18 -05:00
|
|
|
Utc.ymd(2021, 7, 7).and_hms(3, 0, 0),
|
|
|
|
]
|
|
|
|
);
|
|
|
|
|
|
|
|
// filter only by end
|
|
|
|
assert_eq!(
|
|
|
|
db.entries_full(None, Some(end)).unwrap().into_iter().map(|e| e.start).collect::<Vec<_>>(),
|
|
|
|
vec![
|
|
|
|
Utc.ymd(2021, 7, 7).and_hms(1, 0, 0),
|
|
|
|
Utc.ymd(2021, 7, 7).and_hms(2, 0, 0),
|
2021-07-16 18:07:11 -05:00
|
|
|
Utc.ymd(2021, 7, 7).and_hms(1, 0, 0),
|
2021-07-07 11:54:18 -05:00
|
|
|
Utc.ymd(2021, 7, 7).and_hms(2, 0, 0),
|
|
|
|
]
|
|
|
|
);
|
|
|
|
|
|
|
|
// no filter
|
|
|
|
assert_eq!(
|
|
|
|
db.entries_full(None, None).unwrap().into_iter().map(|e| e.start).collect::<Vec<_>>(),
|
|
|
|
vec![
|
|
|
|
Utc.ymd(2021, 7, 7).and_hms(1, 0, 0),
|
|
|
|
Utc.ymd(2021, 7, 7).and_hms(2, 0, 0),
|
|
|
|
Utc.ymd(2021, 7, 7).and_hms(3, 0, 0),
|
2021-07-16 18:07:11 -05:00
|
|
|
Utc.ymd(2021, 7, 7).and_hms(1, 0, 0),
|
|
|
|
Utc.ymd(2021, 7, 7).and_hms(2, 0, 0),
|
2021-07-07 11:54:18 -05:00
|
|
|
Utc.ymd(2021, 7, 7).and_hms(3, 0, 0),
|
|
|
|
]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|