atuin/src/local/database.rs

151 lines
3.8 KiB
Rust
Raw Normal View History

use std::path::Path;
2021-02-13 10:02:52 -07:00
use eyre::{eyre, Result};
use rusqlite::NO_PARAMS;
use rusqlite::{params, Connection};
use crate::History;
pub trait Database {
fn save(&self, h: History) -> Result<()>;
2021-02-13 10:02:52 -07:00
fn load(&self, id: &str) -> Result<History>;
fn list(&self) -> Result<()>;
2021-02-13 10:02:52 -07:00
fn update(&self, h: History) -> Result<()>;
}
// Intended for use on a developer machine and not a sync server.
// TODO: implement IntoIterator
pub struct SqliteDatabase {
conn: Connection,
}
impl SqliteDatabase {
pub fn new(path: impl AsRef<Path>) -> Result<SqliteDatabase> {
let path = path.as_ref();
debug!("opening sqlite database at {:?}", path);
let create = !path.exists();
if create {
if let Some(dir) = path.parent() {
std::fs::create_dir_all(dir)?;
}
}
let conn = Connection::open(path)?;
if create {
Self::setup_db(&conn)?;
}
Ok(SqliteDatabase { conn })
}
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,
timestamp integer not null,
2021-02-13 10:02:52 -07:00
duration integer not null,
exit integer not null,
command text not null,
cwd text not null
)",
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,
timestamp,
2021-02-13 10:02:52 -07:00
duration,
exit,
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],
)?;
Ok(())
}
fn list(&self) -> Result<()> {
debug!("listing history");
let mut stmt = self
.conn
2021-02-13 10:02:52 -07:00
.prepare("SELECT id, timestamp, duration, exit, command, cwd FROM history")?;
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)?,
})
})?;
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
);
}
Ok(())
}
}