diff --git a/Cargo.lock b/Cargo.lock index 07527f7..38f9cd7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -121,6 +121,15 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "directories" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fed639d60b58d0f53498ab13d26f621fd77569cc6edb031f4cc36a2ad9da0f" +dependencies = [ + "dirs-sys", +] + [[package]] name = "dirs" version = "2.0.2" @@ -438,6 +447,7 @@ name = "shync" version = "0.1.0" dependencies = [ "chrono", + "directories", "eyre", "log", "pretty_env_logger", diff --git a/Cargo.toml b/Cargo.toml index f91f12c..6c32eec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,3 +14,4 @@ eyre = "0.6.1" shellexpand = "2.0.0" rusqlite = "0.24.0" structopt = "0.3.15" +directories = "3.0.1" diff --git a/src/local/database.rs b/src/local/database.rs index b84bf99..b94a644 100644 --- a/src/local/database.rs +++ b/src/local/database.rs @@ -1,12 +1,11 @@ use std::path::Path; use eyre::Result; -use shellexpand; -use rusqlite::{params, Connection}; use rusqlite::NO_PARAMS; +use rusqlite::{params, Connection}; -use super::history::History; +use crate::History; pub trait Database { fn save(&self, h: History) -> Result<()>; @@ -19,23 +18,25 @@ pub struct SqliteDatabase { conn: Connection, } -impl SqliteDatabase{ - pub fn new(path: &str) -> Result { - let path = shellexpand::full(path)?; +impl SqliteDatabase { + pub fn new(path: impl AsRef) -> Result { let path = path.as_ref(); - debug!("opening sqlite database at {:?}", path); - let create = !Path::new(path).exists(); + 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: conn, - }) + Ok(SqliteDatabase { conn }) } fn setup_db(conn: &Connection) -> Result<()> { @@ -43,11 +44,11 @@ impl SqliteDatabase{ conn.execute( "create table if not exists history ( - id integer primary key, - timestamp integer not null, - command text not null, - cwd text not null - )", + id integer primary key, + timestamp integer not null, + command text not null, + cwd text not null + )", NO_PARAMS, )?; @@ -61,19 +62,22 @@ impl Database for SqliteDatabase { self.conn.execute( "insert into history ( - timestamp, + timestamp, command, cwd - ) values (?1, ?2, ?3)", - params![h.timestamp, h.command, h.cwd])?; + ) values (?1, ?2, ?3)", + params![h.timestamp, h.command, h.cwd], + )?; Ok(()) } fn list(&self) -> Result<()> { debug!("listing history"); - - let mut stmt = self.conn.prepare("SELECT timestamp, command, cwd FROM history")?; + + let mut stmt = self + .conn + .prepare("SELECT timestamp, command, cwd FROM history")?; let history_iter = stmt.query_map(params![], |row| { Ok(History { timestamp: row.get(0)?, diff --git a/src/local/history.rs b/src/local/history.rs index 61438b6..e84d718 100644 --- a/src/local/history.rs +++ b/src/local/history.rs @@ -8,11 +8,11 @@ pub struct History { } impl History { - pub fn new(command: &str, cwd: &str) -> History { + pub fn new(command: String, cwd: String) -> History { History { timestamp: chrono::Utc::now().timestamp_millis(), - command: command.to_string(), - cwd: cwd.to_string(), + command, + cwd, } } } diff --git a/src/local/mod.rs b/src/local/mod.rs index 8854aa8..f587d01 100644 --- a/src/local/mod.rs +++ b/src/local/mod.rs @@ -1,2 +1,2 @@ -pub mod history; pub mod database; +pub mod history; diff --git a/src/main.rs b/src/main.rs index 769c323..2ec14a5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,45 +1,72 @@ use std::env; +use std::path::PathBuf; +use directories::ProjectDirs; +use eyre::{eyre, Result}; use structopt::StructOpt; -use eyre::Result; -#[macro_use] extern crate log; +#[macro_use] +extern crate log; use pretty_env_logger; mod local; -use local::history::History; use local::database::{Database, SqliteDatabase}; +use local::history::History; #[derive(StructOpt)] #[structopt( - author="Ellie Huxtable ", - version="0.1.0", - about="Keep your shell history in sync" + author = "Ellie Huxtable ", + version = "0.1.0", + about = "Keep your shell history in sync" )] -enum Shync { +struct Shync { + #[structopt(long, parse(from_os_str), help = "db file path")] + db: Option, + + #[structopt(subcommand)] + shync: ShyncCmd, +} + +#[derive(StructOpt)] +enum ShyncCmd { #[structopt( about="manipulate shell history", aliases=&["h", "hi", "his", "hist", "histo", "histor"], )] History(HistoryCmd), - #[structopt( - about="import shell history from file", - )] + #[structopt(about = "import shell history from file")] Import, - #[structopt( - about="start a shync server", - )] + #[structopt(about = "start a shync server")] Server, } impl Shync { - fn run(self, db: SqliteDatabase) -> Result<()> { - match self { - Shync::History(history) => history.run(db), - _ => Ok(()) + fn run(self) -> Result<()> { + let db_path = match self.db { + Some(db_path) => { + let path = db_path + .to_str() + .ok_or(eyre!("path {:?} was not valid UTF-8", db_path))?; + let path = shellexpand::full(path)?; + PathBuf::from(path.as_ref()) + } + None => { + let project_dirs = ProjectDirs::from("bike", "ellie", "shync").ok_or(eyre!( + "could not determine db file location\nspecify one using the --db flag" + ))?; + let root = project_dirs.data_dir(); + root.join("history.db") + } + }; + + let db = SqliteDatabase::new(db_path)?; + + match self.shync { + ShyncCmd::History(history) => history.run(db), + _ => Ok(()), } } } @@ -50,9 +77,7 @@ enum HistoryCmd { about="add a new command to the history", aliases=&["a", "ad"], )] - Add { - command: Vec, - }, + Add { command: Vec }, #[structopt( about="list all items in history", @@ -64,14 +89,10 @@ enum HistoryCmd { impl HistoryCmd { fn run(self, db: SqliteDatabase) -> Result<()> { match self { - HistoryCmd::Add{command: words} => { + HistoryCmd::Add { command: words } => { let command = words.join(" "); - - let cwd = env::current_dir()?; - let h = History::new( - command.as_str(), - cwd.display().to_string().as_str(), - ); + let cwd = env::current_dir()?.display().to_string(); + let h = History::new(command, cwd); debug!("adding history: {:?}", h); db.save(h)?; @@ -79,7 +100,7 @@ impl HistoryCmd { Ok(()) } - HistoryCmd::List => db.list() + HistoryCmd::List => db.list(), } } } @@ -87,6 +108,5 @@ impl HistoryCmd { fn main() -> Result<()> { pretty_env_logger::init(); - let db = SqliteDatabase::new("~/.history.db")?; - Shync::from_args().run(db) + Shync::from_args().run() }