diff options
| author | Ellie Huxtable <e@elm.sh> | 2020-10-05 00:59:28 +0100 |
|---|---|---|
| committer | Ellie Huxtable <e@elm.sh> | 2020-10-05 00:59:28 +0100 |
| commit | 27b9a9430518c071a2a7de7f46dfd8aac3cead80 (patch) | |
| tree | 0551a4979606d7bf351149df10cf08f47dc9e665 /src | |
| download | atuin-27b9a9430518c071a2a7de7f46dfd8aac3cead80.zip | |
Initial commit
Currently writing shell history to a sqlite db :)
Could do with:
1) store exit code
2) store duration
3) tidy up main
4) ...remote stuff
Diffstat (limited to 'src')
| -rw-r--r-- | src/local/database.rs | 93 | ||||
| -rw-r--r-- | src/local/history.rs | 18 | ||||
| -rw-r--r-- | src/local/mod.rs | 2 | ||||
| -rw-r--r-- | src/main.rs | 75 |
4 files changed, 188 insertions, 0 deletions
diff --git a/src/local/database.rs b/src/local/database.rs new file mode 100644 index 00000000..b84bf99c --- /dev/null +++ b/src/local/database.rs @@ -0,0 +1,93 @@ +use std::path::Path; + +use eyre::Result; +use shellexpand; + +use rusqlite::{params, Connection}; +use rusqlite::NO_PARAMS; + +use super::history::History; + +pub trait Database { + fn save(&self, h: History) -> Result<()>; + fn list(&self) -> 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: &str) -> Result<SqliteDatabase> { + let path = shellexpand::full(path)?; + let path = path.as_ref(); + + debug!("opening sqlite database at {:?}", path); + + let create = !Path::new(path).exists(); + let conn = Connection::open(path)?; + + if create { + Self::setup_db(&conn)?; + } + + Ok(SqliteDatabase{ + conn: conn, + }) + } + + fn setup_db(conn: &Connection) -> Result<()> { + debug!("running sqlite database setup"); + + conn.execute( + "create table if not exists history ( + id integer primary key, + timestamp 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 ( + timestamp, + command, + 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 history_iter = stmt.query_map(params![], |row| { + Ok(History { + timestamp: row.get(0)?, + command: row.get(1)?, + cwd: row.get(2)?, + }) + })?; + + for h in history_iter { + let h = h.unwrap(); + + println!("{}:{}:{}", h.timestamp, h.cwd, h.command); + } + + Ok(()) + } +} diff --git a/src/local/history.rs b/src/local/history.rs new file mode 100644 index 00000000..61438b6d --- /dev/null +++ b/src/local/history.rs @@ -0,0 +1,18 @@ +use chrono; + +#[derive(Debug)] +pub struct History { + pub timestamp: i64, + pub command: String, + pub cwd: String, +} + +impl History { + pub fn new(command: &str, cwd: &str) -> History { + History { + timestamp: chrono::Utc::now().timestamp_millis(), + command: command.to_string(), + cwd: cwd.to_string(), + } + } +} diff --git a/src/local/mod.rs b/src/local/mod.rs new file mode 100644 index 00000000..8854aa8e --- /dev/null +++ b/src/local/mod.rs @@ -0,0 +1,2 @@ +pub mod history; +pub mod database; diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 00000000..a9b08c00 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,75 @@ +use std::env; + +use clap::{Arg, App, SubCommand}; +use eyre::Result; + +#[macro_use] extern crate log; +use pretty_env_logger; + +mod local; + +use local::history::History; +use local::database::{Database, SqliteDatabase}; + +fn main() -> Result<()> { + pretty_env_logger::init(); + + let db = SqliteDatabase::new("~/.history.db")?; + + let matches = App::new("Shync") + .version("0.1.0") + .author("Ellie Huxtable <e@elm.sh>") + .about("Keep your shell history in sync") + .subcommand( + SubCommand::with_name("history") + .aliases(&["h", "hi", "his", "hist", "histo", "histor"]) + .about("manipulate shell history") + .subcommand( + SubCommand::with_name("add") + .aliases(&["a", "ad"]) + .about("add a new command to the history") + .arg( + Arg::with_name("command") + .multiple(true) + .required(true) + ) + ) + .subcommand( + SubCommand::with_name("list") + .aliases(&["l", "li", "lis"]) + .about("list all items in history") + ) + ) + .subcommand( + SubCommand::with_name("import") + .about("import shell history from file") + ) + .subcommand( + SubCommand::with_name("server") + .about("start a shync server") + ) + .get_matches(); + + + if let Some(m) = matches.subcommand_matches("history") { + if let Some(m) = m.subcommand_matches("add") { + let words: Vec<&str> = m.values_of("command").unwrap().collect(); + let command = words.join(" "); + + let cwd = env::current_dir()?; + let h = History::new( + command.as_str(), + cwd.display().to_string().as_str(), + ); + + debug!("adding history: {:?}", h); + db.save(h)?; + debug!("saved history to sqlite"); + } + else if let Some(_m) = m.subcommand_matches("list") { + db.list()?; + } + } + + Ok(()) +} |
