2020-10-04 17:59:28 -06:00
|
|
|
use std::path::Path;
|
|
|
|
|
2021-02-13 10:02:52 -07:00
|
|
|
use eyre::{eyre, Result};
|
2020-10-04 17:59:28 -06:00
|
|
|
|
|
|
|
use rusqlite::NO_PARAMS;
|
2020-10-05 10:20:48 -06:00
|
|
|
use rusqlite::{params, Connection};
|
2020-10-04 17:59:28 -06:00
|
|
|
|
2020-10-05 10:20:48 -06:00
|
|
|
use crate::History;
|
2020-10-04 17:59:28 -06:00
|
|
|
|
|
|
|
pub trait Database {
|
|
|
|
fn save(&self, h: History) -> Result<()>;
|
2021-02-13 10:02:52 -07:00
|
|
|
fn load(&self, id: &str) -> Result<History>;
|
2020-10-04 17:59:28 -06:00
|
|
|
fn list(&self) -> Result<()>;
|
2021-02-13 10:02:52 -07:00
|
|
|
fn update(&self, h: History) -> Result<()>;
|
2020-10-04 17:59:28 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Intended for use on a developer machine and not a sync server.
|
|
|
|
// TODO: implement IntoIterator
|
|
|
|
pub struct SqliteDatabase {
|
|
|
|
conn: Connection,
|
|
|
|
}
|
|
|
|
|
2020-10-05 10:20:48 -06:00
|
|
|
impl SqliteDatabase {
|
|
|
|
pub fn new(path: impl AsRef<Path>) -> Result<SqliteDatabase> {
|
2020-10-04 17:59:28 -06:00
|
|
|
let path = path.as_ref();
|
|
|
|
debug!("opening sqlite database at {:?}", path);
|
|
|
|
|
2020-10-05 10:20:48 -06:00
|
|
|
let create = !path.exists();
|
|
|
|
if create {
|
|
|
|
if let Some(dir) = path.parent() {
|
|
|
|
std::fs::create_dir_all(dir)?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-04 17:59:28 -06:00
|
|
|
let conn = Connection::open(path)?;
|
|
|
|
|
|
|
|
if create {
|
|
|
|
Self::setup_db(&conn)?;
|
|
|
|
}
|
|
|
|
|
2020-10-05 10:20:48 -06:00
|
|
|
Ok(SqliteDatabase { conn })
|
2020-10-04 17:59:28 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
fn setup_db(conn: &Connection) -> Result<()> {
|
|
|
|
debug!("running sqlite database setup");
|
|
|
|
|
|
|
|
conn.execute(
|
|
|
|
"create table if not exists history (
|
2021-02-13 10:02:52 -07:00
|
|
|
id text primary key,
|
2020-10-05 10:20:48 -06:00
|
|
|
timestamp integer not null,
|
2021-02-13 10:02:52 -07:00
|
|
|
duration integer not null,
|
|
|
|
exit integer not null,
|
2020-10-05 10:20:48 -06:00
|
|
|
command text not null,
|
|
|
|
cwd text not null
|
|
|
|
)",
|
2020-10-04 17:59:28 -06:00
|
|
|
NO_PARAMS,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Database for SqliteDatabase {
|
|
|
|
fn save(&self, h: History) -> Result<()> {
|
|
|
|
debug!("saving history to sqlite");
|
|
|
|
|
|
|
|
self.conn.execute(
|
|
|
|
"insert into history (
|
2021-02-13 10:02:52 -07:00
|
|
|
id,
|
2020-10-05 10:20:48 -06:00
|
|
|
timestamp,
|
2021-02-13 10:02:52 -07:00
|
|
|
duration,
|
|
|
|
exit,
|
2020-10-04 17:59:28 -06:00
|
|
|
command,
|
|
|
|
cwd
|
2021-02-13 10:02:52 -07:00
|
|
|
) values (?1, ?2, ?3, ?4, ?5, ?6)",
|
|
|
|
params![h.id, h.timestamp, h.duration, h.exit, h.command, h.cwd],
|
|
|
|
)?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn load(&self, id: &str) -> Result<History> {
|
|
|
|
debug!("loading history item");
|
|
|
|
|
|
|
|
let mut stmt = self.conn.prepare(
|
|
|
|
"select id, timestamp, duration, exit, command, cwd from history
|
|
|
|
where id = ?1",
|
|
|
|
)?;
|
|
|
|
|
|
|
|
let iter = stmt.query_map(params![id], |row| {
|
|
|
|
Ok(History {
|
|
|
|
id: String::from(id),
|
|
|
|
timestamp: row.get(1)?,
|
|
|
|
duration: row.get(2)?,
|
|
|
|
exit: row.get(3)?,
|
|
|
|
command: row.get(4)?,
|
|
|
|
cwd: row.get(5)?,
|
|
|
|
})
|
|
|
|
})?;
|
|
|
|
|
|
|
|
for i in iter {
|
|
|
|
return Ok(i.unwrap());
|
|
|
|
}
|
|
|
|
|
|
|
|
return Err(eyre!("Failed to fetch history: {}", id));
|
|
|
|
}
|
|
|
|
|
|
|
|
fn update(&self, h: History) -> Result<()> {
|
|
|
|
debug!("updating sqlite history");
|
|
|
|
|
|
|
|
self.conn.execute(
|
|
|
|
"update history
|
|
|
|
set timestamp = ?2, duration = ?3, exit = ?4, command = ?5, cwd = ?6
|
|
|
|
where id = ?1",
|
|
|
|
params![h.id, h.timestamp, h.duration, h.exit, h.command, h.cwd],
|
2020-10-05 10:20:48 -06:00
|
|
|
)?;
|
2020-10-04 17:59:28 -06:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn list(&self) -> Result<()> {
|
|
|
|
debug!("listing history");
|
2020-10-05 10:20:48 -06:00
|
|
|
|
|
|
|
let mut stmt = self
|
|
|
|
.conn
|
2021-02-13 10:02:52 -07:00
|
|
|
.prepare("SELECT id, timestamp, duration, exit, command, cwd FROM history")?;
|
|
|
|
|
2020-10-04 17:59:28 -06:00
|
|
|
let history_iter = stmt.query_map(params![], |row| {
|
|
|
|
Ok(History {
|
2021-02-13 10:02:52 -07:00
|
|
|
id: row.get(0)?,
|
|
|
|
timestamp: row.get(1)?,
|
|
|
|
duration: row.get(2)?,
|
|
|
|
exit: row.get(3)?,
|
|
|
|
command: row.get(4)?,
|
|
|
|
cwd: row.get(5)?,
|
2020-10-04 17:59:28 -06:00
|
|
|
})
|
|
|
|
})?;
|
|
|
|
|
|
|
|
for h in history_iter {
|
|
|
|
let h = h.unwrap();
|
|
|
|
|
2021-02-13 10:02:52 -07:00
|
|
|
println!(
|
|
|
|
"{} | {} | {} | {} | {}",
|
|
|
|
h.timestamp, h.cwd, h.duration, h.exit, h.command
|
|
|
|
);
|
2020-10-04 17:59:28 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|