aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorEllie Huxtable <e@elm.sh>2020-10-05 00:59:28 +0100
committerEllie Huxtable <e@elm.sh>2020-10-05 00:59:28 +0100
commit27b9a9430518c071a2a7de7f46dfd8aac3cead80 (patch)
tree0551a4979606d7bf351149df10cf08f47dc9e665 /src
downloadatuin-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.rs93
-rw-r--r--src/local/history.rs18
-rw-r--r--src/local/mod.rs2
-rw-r--r--src/main.rs75
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(())
+}