From a21737e2b7f8d1e426726bdd7536033f299d476a Mon Sep 17 00:00:00 2001 From: Ellie Huxtable Date: Tue, 20 Apr 2021 21:53:07 +0100 Subject: Use cargo workspaces (#37) * Switch to Cargo workspaces Breaking things into "client", "server" and "common" makes managing the codebase much easier! client - anything running on a user's machine for adding history server - handles storing/syncing history and running a HTTP server common - request/response API definitions, common utils, etc * Update dockerfile --- src/local/database.rs | 272 -------------------------------------------------- 1 file changed, 272 deletions(-) delete mode 100644 src/local/database.rs (limited to 'src/local/database.rs') diff --git a/src/local/database.rs b/src/local/database.rs deleted file mode 100644 index abc22bb8..00000000 --- a/src/local/database.rs +++ /dev/null @@ -1,272 +0,0 @@ -use chrono::prelude::*; -use chrono::Utc; -use std::path::Path; - -use eyre::Result; - -use rusqlite::{params, Connection}; -use rusqlite::{Params, Transaction}; - -use super::history::History; - -pub trait Database { - fn save(&mut self, h: &History) -> Result<()>; - fn save_bulk(&mut self, h: &[History]) -> Result<()>; - - fn load(&self, id: &str) -> Result; - fn list(&self) -> Result>; - fn range(&self, from: chrono::DateTime, to: chrono::DateTime) - -> Result>; - - fn query(&self, query: &str, params: impl Params) -> Result>; - fn update(&self, h: &History) -> Result<()>; - fn history_count(&self) -> Result; - - fn first(&self) -> Result; - fn last(&self) -> Result; - fn before(&self, timestamp: chrono::DateTime, count: i64) -> Result>; - - fn prefix_search(&self, query: &str) -> Result>; -} - -// Intended for use on a developer machine and not a sync server. -// TODO: implement IntoIterator -pub struct Sqlite { - conn: Connection, -} - -impl Sqlite { - pub fn new(path: impl AsRef) -> Result { - 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)?; - - Self::setup_db(&conn)?; - - Ok(Self { conn }) - } - - fn setup_db(conn: &Connection) -> Result<()> { - debug!("running sqlite database setup"); - - conn.execute( - "create table if not exists history ( - id text primary key, - timestamp integer not null, - duration integer not null, - exit integer not null, - command text not null, - cwd text not null, - session text not null, - hostname text not null, - - unique(timestamp, cwd, command) - )", - [], - )?; - - conn.execute( - "create table if not exists history_encrypted ( - id text primary key, - data blob not null - )", - [], - )?; - - Ok(()) - } - - fn save_raw(tx: &Transaction, h: &History) -> Result<()> { - tx.execute( - "insert or ignore into history ( - id, - timestamp, - duration, - exit, - command, - cwd, - session, - hostname - ) values (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)", - params![ - h.id, - h.timestamp.timestamp_nanos(), - h.duration, - h.exit, - h.command, - h.cwd, - h.session, - h.hostname - ], - )?; - - Ok(()) - } -} - -impl Database for Sqlite { - fn save(&mut self, h: &History) -> Result<()> { - debug!("saving history to sqlite"); - - let tx = self.conn.transaction()?; - Self::save_raw(&tx, h)?; - tx.commit()?; - - Ok(()) - } - - fn save_bulk(&mut self, h: &[History]) -> Result<()> { - debug!("saving history to sqlite"); - - let tx = self.conn.transaction()?; - for i in h { - Self::save_raw(&tx, i)? - } - tx.commit()?; - - Ok(()) - } - - fn load(&self, id: &str) -> Result { - debug!("loading history item"); - - let mut stmt = self.conn.prepare( - "select id, timestamp, duration, exit, command, cwd, session, hostname from history - where id = ?1", - )?; - - let history = stmt.query_row(params![id], |row| { - history_from_sqlite_row(Some(id.to_string()), row) - })?; - - Ok(history) - } - - 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, session = ?7, hostname = ?8 - where id = ?1", - params![h.id, h.timestamp.timestamp_nanos(), h.duration, h.exit, h.command, h.cwd, h.session, h.hostname], - )?; - - Ok(()) - } - - fn list(&self) -> Result> { - debug!("listing history"); - - let mut stmt = self - .conn - .prepare("SELECT * FROM history order by timestamp asc")?; - - let history_iter = stmt.query_map(params![], |row| history_from_sqlite_row(None, row))?; - - Ok(history_iter.filter_map(Result::ok).collect()) - } - - fn range( - &self, - from: chrono::DateTime, - to: chrono::DateTime, - ) -> Result> { - debug!("listing history from {:?} to {:?}", from, to); - - let mut stmt = self.conn.prepare( - "SELECT * FROM history where timestamp >= ?1 and timestamp <= ?2 order by timestamp asc", - )?; - - let history_iter = stmt.query_map( - params![from.timestamp_nanos(), to.timestamp_nanos()], - |row| history_from_sqlite_row(None, row), - )?; - - Ok(history_iter.filter_map(Result::ok).collect()) - } - - fn first(&self) -> Result { - let mut stmt = self - .conn - .prepare("SELECT * FROM history order by timestamp asc limit 1")?; - - let history = stmt.query_row(params![], |row| history_from_sqlite_row(None, row))?; - - Ok(history) - } - - fn last(&self) -> Result { - let mut stmt = self - .conn - .prepare("SELECT * FROM history order by timestamp desc limit 1")?; - - let history = stmt.query_row(params![], |row| history_from_sqlite_row(None, row))?; - - Ok(history) - } - - fn before(&self, timestamp: chrono::DateTime, count: i64) -> Result> { - let mut stmt = self - .conn - .prepare("SELECT * FROM history where timestamp < ? order by timestamp desc limit ?")?; - - let history_iter = stmt.query_map(params![timestamp.timestamp_nanos(), count], |row| { - history_from_sqlite_row(None, row) - })?; - - Ok(history_iter.filter_map(Result::ok).collect()) - } - - fn query(&self, query: &str, params: impl Params) -> Result> { - let mut stmt = self.conn.prepare(query)?; - - let history_iter = stmt.query_map(params, |row| history_from_sqlite_row(None, row))?; - - Ok(history_iter.filter_map(Result::ok).collect()) - } - - fn prefix_search(&self, query: &str) -> Result> { - self.query( - "select * from history where command like ?1 || '%' order by timestamp asc limit 1000", - &[query], - ) - } - - fn history_count(&self) -> Result { - let res: i64 = - self.conn - .query_row_and_then("select count(1) from history;", params![], |row| row.get(0))?; - - Ok(res) - } -} - -fn history_from_sqlite_row( - id: Option, - row: &rusqlite::Row, -) -> Result { - let id = match id { - Some(id) => id, - None => row.get(0)?, - }; - - Ok(History { - id, - timestamp: Utc.timestamp_nanos(row.get(1)?), - duration: row.get(2)?, - exit: row.get(3)?, - command: row.get(4)?, - cwd: row.get(5)?, - session: row.get(6)?, - hostname: row.get(7)?, - }) -} -- cgit v1.3.1