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 {
|
2021-02-13 13:21:49 -07:00
|
|
|
fn save(&mut self, h: History) -> Result<()>;
|
2021-02-14 08:15:26 -07:00
|
|
|
fn save_bulk(&mut self, h: &[History]) -> Result<()>;
|
2021-02-13 10:02:52 -07:00
|
|
|
fn load(&self, id: &str) -> Result<History>;
|
2021-02-13 15:06:27 -07:00
|
|
|
fn list(&self, distinct: bool) -> 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
|
2021-02-14 08:15:26 -07:00
|
|
|
pub struct Sqlite {
|
2020-10-04 17:59:28 -06:00
|
|
|
conn: Connection,
|
|
|
|
}
|
|
|
|
|
2021-02-14 08:15:26 -07:00
|
|
|
impl Sqlite {
|
|
|
|
pub fn new(path: impl AsRef<Path>) -> Result<Self> {
|
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)?;
|
|
|
|
}
|
|
|
|
|
2021-02-14 08:15:26 -07:00
|
|
|
Ok(Self { 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,
|
2021-02-13 12:37:00 -07:00
|
|
|
cwd text not null,
|
2021-02-13 13:21:49 -07:00
|
|
|
session text not null,
|
|
|
|
hostname text not null,
|
2021-02-13 12:37:00 -07:00
|
|
|
|
|
|
|
unique(timestamp, cwd, command)
|
2020-10-05 10:20:48 -06:00
|
|
|
)",
|
2020-10-04 17:59:28 -06:00
|
|
|
NO_PARAMS,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-14 08:15:26 -07:00
|
|
|
impl Database for Sqlite {
|
2021-02-13 13:21:49 -07:00
|
|
|
fn save(&mut self, h: History) -> Result<()> {
|
2020-10-04 17:59:28 -06:00
|
|
|
debug!("saving history to sqlite");
|
2021-02-13 13:21:49 -07:00
|
|
|
let v = vec![h];
|
2020-10-04 17:59:28 -06:00
|
|
|
|
2021-02-13 13:21:49 -07:00
|
|
|
self.save_bulk(&v)
|
2021-02-13 10:02:52 -07:00
|
|
|
}
|
|
|
|
|
2021-02-14 08:15:26 -07:00
|
|
|
fn save_bulk(&mut self, h: &[History]) -> Result<()> {
|
2021-02-13 12:37:00 -07:00
|
|
|
debug!("saving history to sqlite");
|
|
|
|
|
|
|
|
let tx = self.conn.transaction()?;
|
|
|
|
|
|
|
|
for i in h {
|
|
|
|
tx.execute(
|
|
|
|
"insert or ignore into history (
|
|
|
|
id,
|
|
|
|
timestamp,
|
|
|
|
duration,
|
|
|
|
exit,
|
|
|
|
command,
|
2021-02-13 13:21:49 -07:00
|
|
|
cwd,
|
|
|
|
session,
|
|
|
|
hostname
|
|
|
|
) values (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)",
|
|
|
|
params![
|
|
|
|
i.id,
|
|
|
|
i.timestamp,
|
|
|
|
i.duration,
|
|
|
|
i.exit,
|
|
|
|
i.command,
|
|
|
|
i.cwd,
|
|
|
|
i.session,
|
|
|
|
i.hostname
|
|
|
|
],
|
2021-02-13 12:37:00 -07:00
|
|
|
)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
tx.commit()?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-02-13 10:02:52 -07:00
|
|
|
fn load(&self, id: &str) -> Result<History> {
|
|
|
|
debug!("loading history item");
|
|
|
|
|
|
|
|
let mut stmt = self.conn.prepare(
|
2021-02-13 13:21:49 -07:00
|
|
|
"select id, timestamp, duration, exit, command, cwd, session, hostname from history
|
2021-02-13 10:02:52 -07:00
|
|
|
where id = ?1",
|
|
|
|
)?;
|
|
|
|
|
2021-02-14 08:15:26 -07:00
|
|
|
let mut iter = stmt.query_map(params![id], |row| {
|
2021-02-13 10:02:52 -07:00
|
|
|
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)?,
|
2021-02-13 13:21:49 -07:00
|
|
|
session: row.get(6)?,
|
|
|
|
hostname: row.get(7)?,
|
2021-02-13 10:02:52 -07:00
|
|
|
})
|
|
|
|
})?;
|
|
|
|
|
2021-02-14 08:15:26 -07:00
|
|
|
let history = iter.next().unwrap();
|
2021-02-13 10:02:52 -07:00
|
|
|
|
2021-02-14 08:15:26 -07:00
|
|
|
match history {
|
|
|
|
Ok(i) => Ok(i),
|
|
|
|
Err(e) => Err(eyre!("could not find item: {}", e)),
|
|
|
|
}
|
2021-02-13 10:02:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
fn update(&self, h: History) -> Result<()> {
|
|
|
|
debug!("updating sqlite history");
|
|
|
|
|
|
|
|
self.conn.execute(
|
|
|
|
"update history
|
2021-02-13 13:21:49 -07:00
|
|
|
set timestamp = ?2, duration = ?3, exit = ?4, command = ?5, cwd = ?6, session = ?7, hostname = ?8
|
2021-02-13 10:02:52 -07:00
|
|
|
where id = ?1",
|
2021-02-13 13:21:49 -07:00
|
|
|
params![h.id, h.timestamp, h.duration, h.exit, h.command, h.cwd, h.session, h.hostname],
|
2020-10-05 10:20:48 -06:00
|
|
|
)?;
|
2020-10-04 17:59:28 -06:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-02-13 15:06:27 -07:00
|
|
|
fn list(&self, distinct: bool) -> Result<()> {
|
2020-10-04 17:59:28 -06:00
|
|
|
debug!("listing history");
|
2020-10-05 10:20:48 -06:00
|
|
|
|
2021-02-14 08:15:26 -07:00
|
|
|
let mut stmt = if distinct {
|
|
|
|
self.conn
|
|
|
|
.prepare("SELECT command FROM history order by timestamp asc")?
|
|
|
|
} else {
|
|
|
|
self.conn
|
|
|
|
.prepare("SELECT distinct command FROM history order by timestamp asc")?
|
2021-02-13 15:06:27 -07:00
|
|
|
};
|
2021-02-13 10:02:52 -07:00
|
|
|
|
2020-10-04 17:59:28 -06:00
|
|
|
let history_iter = stmt.query_map(params![], |row| {
|
2021-02-13 15:06:27 -07:00
|
|
|
let command: String = row.get(0)?;
|
|
|
|
|
|
|
|
Ok(command)
|
2020-10-04 17:59:28 -06:00
|
|
|
})?;
|
|
|
|
|
|
|
|
for h in history_iter {
|
|
|
|
let h = h.unwrap();
|
|
|
|
|
2021-02-13 15:06:27 -07:00
|
|
|
println!("{}", h);
|
2020-10-04 17:59:28 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|