database adjustments and migrations
This commit is contained in:
parent
3505ee396c
commit
91a99ed5ea
|
@ -37,14 +37,15 @@ impl<'a> Command<'a> for InCommand {
|
||||||
O: Write,
|
O: Write,
|
||||||
E: Write,
|
E: Write,
|
||||||
{
|
{
|
||||||
let at = args.at.unwrap_or(Utc::now());
|
let start = args.at.unwrap_or(Utc::now());
|
||||||
|
let sheet = db.current_sheet()?.unwrap_or("default".into());
|
||||||
let note = if let Some(note) = args.note {
|
let note = if let Some(note) = args.note {
|
||||||
note
|
note
|
||||||
} else {
|
} else {
|
||||||
editor::get_string(config)?
|
editor::get_string(config)?
|
||||||
};
|
};
|
||||||
|
|
||||||
db.entry_insert(at, note)?;
|
db.entry_insert(start, None, Some(note), sheet)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::path::Path;
|
||||||
use rusqlite::{Connection, ToSql};
|
use rusqlite::{Connection, ToSql};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
|
|
||||||
use crate::error;
|
use crate::error::{Error, Result};
|
||||||
use crate::models::{Entry, Meta};
|
use crate::models::{Entry, Meta};
|
||||||
|
|
||||||
pub enum DBVersion {
|
pub enum DBVersion {
|
||||||
|
@ -13,43 +13,70 @@ pub enum DBVersion {
|
||||||
|
|
||||||
pub trait Database {
|
pub trait Database {
|
||||||
/// This is used to create tables and insert rows
|
/// This is used to create tables and insert rows
|
||||||
fn execute(&mut self, query: &str, params: &[&dyn ToSql]) -> error::Result<()>;
|
fn execute(&mut self, query: &str, params: &[&dyn ToSql]) -> Result<()>;
|
||||||
|
|
||||||
/// And this is used to retrieve data
|
/// And this is used to retrieve data
|
||||||
fn entry_query(&self, query: &str, params: &[&dyn ToSql]) -> error::Result<Vec<Entry>>;
|
fn entry_query(&self, query: &str, params: &[&dyn ToSql]) -> Result<Vec<Entry>>;
|
||||||
fn meta_query(&self, query: &str, params: &[&dyn ToSql]) -> error::Result<Vec<Meta>>;
|
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)
|
||||||
|
)
|
||||||
|
", &[])?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------
|
||||||
// Entry queries
|
// Entry queries
|
||||||
|
// -------------
|
||||||
fn entries_by_sheet(&self, sheet: &str) -> error::Result<Vec<Entry>> {
|
fn entries_by_sheet(&self, sheet: &str) -> Result<Vec<Entry>> {
|
||||||
self.entry_query("select * from entries where sheet=?1 order by start asc", &[&sheet])
|
self.entry_query("select * from entries where sheet=?1 order by start asc", &[&sheet])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn entries_all_visible(&self) -> error::Result<Vec<Entry>> {
|
fn entries_all_visible(&self) -> Result<Vec<Entry>> {
|
||||||
self.entry_query("select * from entries where sheet not like '!_%' escape \"!\" order by sheet asc, start asc", &[])
|
self.entry_query("select * from entries where sheet not like '!_%' escape \"!\" order by sheet asc, start asc", &[])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn entries_full(&self) -> error::Result<Vec<Entry>> {
|
fn entries_full(&self) -> Result<Vec<Entry>> {
|
||||||
self.entry_query("select * from entries order by sheet asc, start asc", &[])
|
self.entry_query("select * from entries order by sheet asc, start asc", &[])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn entry_insert(&mut self, at: DateTime<Utc>, note: String) -> error::Result<()> {
|
fn entry_insert(&mut self, start: DateTime<Utc>, end: Option<DateTime<Utc>>, note: Option<String>, sheet: String) -> Result<()> {
|
||||||
self.execute("", &[])
|
self.execute("insert into entries (start, end, note, sheet) values (?1, ?2, ?3, ?4)", &[
|
||||||
|
&start, &end, ¬e, &sheet,
|
||||||
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Meta queries
|
// Meta queries
|
||||||
fn current_sheet(&self) -> error::Result<Option<String>> {
|
fn current_sheet(&self) -> Result<Option<String>> {
|
||||||
let results = self.meta_query("select * from meta where key='current_sheet'", &[])?;
|
let results = self.meta_query("select * from meta where key='current_sheet'", &[])?;
|
||||||
|
|
||||||
Ok(results.into_iter().next().map(|m| m.value))
|
Ok(results.into_iter().next().map(|m| m.value))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn version(&self) -> error::Result<DBVersion> {
|
fn version(&self) -> Result<DBVersion> {
|
||||||
let results = self.meta_query("select * from meta where key='database_version'", &[])?;
|
let results = self.meta_query("select * from meta where key='database_version'", &[])?;
|
||||||
|
|
||||||
if let Some(v) = results.into_iter().next().map(|m| m.value) {
|
if let Some(v) = results.into_iter().next().map(|m| m.value) {
|
||||||
Ok(DBVersion::Version(v.parse().map_err(|e| {
|
Ok(DBVersion::Version(v.parse().map_err(|_| {
|
||||||
error::Error::CorruptedData(format!(
|
Error::CorruptedData(format!(
|
||||||
"Found value '{}' for key 'database_version' in meta table, which is not a valid integer",
|
"Found value '{}' for key 'database_version' in meta table, which is not a valid integer",
|
||||||
v
|
v
|
||||||
))
|
))
|
||||||
|
@ -65,13 +92,13 @@ pub struct SqliteDatabase {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SqliteDatabase {
|
impl SqliteDatabase {
|
||||||
pub fn from_memory() -> error::Result<impl Database> {
|
pub fn from_memory() -> Result<impl Database> {
|
||||||
Ok(SqliteDatabase {
|
Ok(SqliteDatabase {
|
||||||
connection: Connection::open_in_memory()?,
|
connection: Connection::open_in_memory()?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_path<P: AsRef<Path>>(path: P) -> error::Result<impl Database> {
|
pub fn from_path<P: AsRef<Path>>(path: P) -> Result<impl Database> {
|
||||||
Ok(SqliteDatabase {
|
Ok(SqliteDatabase {
|
||||||
connection: Connection::open(path)?,
|
connection: Connection::open(path)?,
|
||||||
})
|
})
|
||||||
|
@ -79,13 +106,13 @@ impl SqliteDatabase {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Database for SqliteDatabase {
|
impl Database for SqliteDatabase {
|
||||||
fn execute(&mut self, query: &str, params: &[&dyn ToSql]) -> error::Result<()> {
|
fn execute(&mut self, query: &str, params: &[&dyn ToSql]) -> Result<()> {
|
||||||
self.connection.execute(query, params)?;
|
self.connection.execute(query, params)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn entry_query(&self, query: &str, params: &[&dyn ToSql]) -> error::Result<Vec<Entry>> {
|
fn entry_query(&self, query: &str, params: &[&dyn ToSql]) -> Result<Vec<Entry>> {
|
||||||
let mut stmt = self.connection.prepare(query)?;
|
let mut stmt = self.connection.prepare(query)?;
|
||||||
|
|
||||||
let results = stmt.query_map(params, |row| {
|
let results = stmt.query_map(params, |row| {
|
||||||
|
@ -102,7 +129,7 @@ impl Database for SqliteDatabase {
|
||||||
Ok(results)
|
Ok(results)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn meta_query(&self, query: &str, params: &[&dyn ToSql]) -> error::Result<Vec<Meta>> {
|
fn meta_query(&self, query: &str, params: &[&dyn ToSql]) -> Result<Vec<Meta>> {
|
||||||
let mut stmt = self.connection.prepare(query)?;
|
let mut stmt = self.connection.prepare(query)?;
|
||||||
|
|
||||||
let results = stmt.query_map(params, |row| Ok(Meta {
|
let results = stmt.query_map(params, |row| Ok(Meta {
|
||||||
|
|
|
@ -76,15 +76,16 @@ pub fn print_formatted<W: Write>(entries: Vec<Entry>, out: &mut W, now: DateTime
|
||||||
let duration = entry.end.unwrap_or(now) - entry.start;
|
let duration = entry.end.unwrap_or(now) - entry.start;
|
||||||
daily = daily + duration;
|
daily = daily + duration;
|
||||||
let duration = format_duration(duration);
|
let duration = format_duration(duration);
|
||||||
|
let note = entry.note.unwrap_or("".into());
|
||||||
|
|
||||||
let id = if ids { entry.id.to_string() } else { "".into() };
|
let id = if ids { entry.id.to_string() } else { "".into() };
|
||||||
|
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
let date = date.format("%a %b %d, %Y").to_string();
|
let date = date.format("%a %b %d, %Y").to_string();
|
||||||
|
|
||||||
lines.push([id, date, start, end, duration, entry.note]);
|
lines.push([id, date, start, end, duration, note]);
|
||||||
} else {
|
} else {
|
||||||
lines.push([id, "".into(), start, end, duration, entry.note]);
|
lines.push([id, "".into(), start, end, duration, note]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -297,7 +298,7 @@ mod tests {
|
||||||
sheet: "default".into(),
|
sheet: "default".into(),
|
||||||
start: Utc.ymd(2008, 10, 5).and_hms(16, 0, 0),
|
start: Utc.ymd(2008, 10, 5).and_hms(16, 0, 0),
|
||||||
end: Some(Utc.ymd(2008, 10, 5).and_hms(18, 0, 0)),
|
end: Some(Utc.ymd(2008, 10, 5).and_hms(18, 0, 0)),
|
||||||
note: LONG_NOTE.into(),
|
note: Some(LONG_NOTE.into()),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -330,7 +331,7 @@ mod tests {
|
||||||
sheet: "default".into(),
|
sheet: "default".into(),
|
||||||
start: Utc.ymd(2008, 10, 5).and_hms(16, 0, 0),
|
start: Utc.ymd(2008, 10, 5).and_hms(16, 0, 0),
|
||||||
end: Some(Utc.ymd(2008, 10, 5).and_hms(18, 0, 0)),
|
end: Some(Utc.ymd(2008, 10, 5).and_hms(18, 0, 0)),
|
||||||
note: LONG_NOTE.into(),
|
note: Some(LONG_NOTE.into()),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -363,7 +364,7 @@ mod tests {
|
||||||
sheet: "default".into(),
|
sheet: "default".into(),
|
||||||
start: Utc.ymd(2008, 10, 5).and_hms(16, 0, 0),
|
start: Utc.ymd(2008, 10, 5).and_hms(16, 0, 0),
|
||||||
end: Some(Utc.ymd(2008, 10, 5).and_hms(18, 0, 0)),
|
end: Some(Utc.ymd(2008, 10, 5).and_hms(18, 0, 0)),
|
||||||
note: "first line\nand a second line".into(),
|
note: Some("first line\nand a second line".into()),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ use chrono::{DateTime, Utc};
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Entry {
|
pub struct Entry {
|
||||||
pub id: u64,
|
pub id: u64,
|
||||||
pub note: String,
|
pub note: Option<String>,
|
||||||
pub start: DateTime<Utc>,
|
pub start: DateTime<Utc>,
|
||||||
pub end: Option<DateTime<Utc>>,
|
pub end: Option<DateTime<Utc>>,
|
||||||
pub sheet: String,
|
pub sheet: String,
|
||||||
|
@ -21,7 +21,7 @@ impl Entry {
|
||||||
pub fn new_sample(id: u64, start: DateTime<Utc>, end: Option<DateTime<Utc>>) -> Entry {
|
pub fn new_sample(id: u64, start: DateTime<Utc>, end: Option<DateTime<Utc>>) -> Entry {
|
||||||
Entry {
|
Entry {
|
||||||
id,
|
id,
|
||||||
note: format!("entry {}", id),
|
note: Some(format!("entry {}", id)),
|
||||||
start, end,
|
start, end,
|
||||||
sheet: "default".into(),
|
sheet: "default".into(),
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue