diff options
author | Ellie Huxtable <e@elm.sh> | 2021-02-13 20:21:49 +0000 |
---|---|---|
committer | Ellie Huxtable <e@elm.sh> | 2021-02-13 20:24:22 +0000 |
commit | 440c4fc2335d5286d14367e39c99bb4946efe9e3 (patch) | |
tree | 766f33c9c6b87998f22bc146fe7361e798b377df | |
parent | 099afe66ecfb569a8a04b66425ded29665e6a37c (diff) |
Add sessions
-rw-r--r-- | Cargo.lock | 20 | ||||
-rw-r--r-- | Cargo.toml | 3 | ||||
-rw-r--r-- | README.md | 13 | ||||
-rw-r--r-- | src/command/history.rs | 12 | ||||
-rw-r--r-- | src/command/import.rs | 13 | ||||
-rw-r--r-- | src/local/database.rs | 58 | ||||
-rw-r--r-- | src/local/history.rs | 30 | ||||
-rw-r--r-- | src/local/import.rs | 6 | ||||
-rw-r--r-- | src/main.rs | 12 |
9 files changed, 130 insertions, 37 deletions
diff --git a/Cargo.lock b/Cargo.lock index fcf1175d1..bd8b36090 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -49,12 +49,13 @@ dependencies = [ [[package]] name = "atuin" -version = "0.2.0" +version = "0.2.1" dependencies = [ "chrono", "directories", "eyre", "home", + "hostname", "indicatif", "log", "pretty_env_logger", @@ -315,6 +316,17 @@ dependencies = [ ] [[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi", +] + +[[package]] name = "humantime" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -374,6 +386,12 @@ dependencies = [ ] [[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + +[[package]] name = "memchr" version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/Cargo.toml b/Cargo.toml index 57117c29c..c4b67a8cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "atuin" -version = "0.2.0" +version = "0.2.1" authors = ["Ellie Huxtable <e@elm.sh>"] edition = "2018" license = "MIT" @@ -17,6 +17,7 @@ directories = "3.*" uuid = { version = "0.8", features = ["serde", "v4"] } home = "0.5.3" indicatif = "0.15.0" +hostname = "0.3.1" [dependencies.rusqlite] version = "0.24.*" @@ -5,4 +5,15 @@ Through the fathomless deeps of space swims the star turtle Great AβTuin, bearing on its back the four giant elephants who carry on their shoulders the mass of the Discworld. </blockquote> - `atuin` manages and synchronizes your shell history! +`atuin` manages and synchronizes your shell history! Instead of storing +everything in a text file (such as ~/.history), `atuin` uses a sqlite database. +This lets us do all kinds of analysis on it! + +As well as the expected command, this stores + +- duration +- exit code +- working directory +- hostname +- time +- a unique session ID diff --git a/src/command/history.rs b/src/command/history.rs index 72f821c58..4caf4c177 100644 --- a/src/command/history.rs +++ b/src/command/history.rs @@ -32,13 +32,21 @@ pub enum HistoryCmd { } impl HistoryCmd { - pub fn run(&self, db: SqliteDatabase) -> Result<()> { + pub fn run(&self, db: &mut SqliteDatabase) -> Result<()> { match self { HistoryCmd::Start { command: words } => { let command = words.join(" "); let cwd = env::current_dir()?.display().to_string(); - let h = History::new(chrono::Utc::now().timestamp_nanos(), command, cwd, -1, -1); + let h = History::new( + chrono::Utc::now().timestamp_nanos(), + command, + cwd, + -1, + -1, + None, + None, + ); // print the ID // we use this as the key for calling end diff --git a/src/command/import.rs b/src/command/import.rs index 5ece13a8d..5ce0e7493 100644 --- a/src/command/import.rs +++ b/src/command/import.rs @@ -87,12 +87,23 @@ impl ImportCmd { } pub fn run(&self, db: &mut SqliteDatabase) -> Result<()> { + println!(" A'Tuin "); + println!("====================="); + println!(" π "); + println!(" ππππ "); + println!(" π’ "); + println!("====================="); + println!("Importing history..."); + match self { ImportCmd::Auto => { let shell = env::var("SHELL").unwrap_or(String::from("NO_SHELL")); match shell.as_str() { - "/bin/zsh" => self.import_zsh(db), + "/bin/zsh" => { + println!("Detected ZSH"); + self.import_zsh(db) + } _ => { println!("cannot import {} history", shell); diff --git a/src/local/database.rs b/src/local/database.rs index 2a4cc5826..e9882fd79 100644 --- a/src/local/database.rs +++ b/src/local/database.rs @@ -8,7 +8,7 @@ use rusqlite::{params, Connection}; use crate::History; pub trait Database { - fn save(&self, h: History) -> Result<()>; + fn save(&mut self, h: History) -> Result<()>; fn save_bulk(&mut self, h: &Vec<History>) -> Result<()>; fn load(&self, id: &str) -> Result<History>; fn list(&self) -> Result<()>; @@ -53,6 +53,8 @@ impl SqliteDatabase { exit integer not null, command text not null, cwd text not null, + session text not null, + hostname text not null, unique(timestamp, cwd, command) )", @@ -64,22 +66,11 @@ impl SqliteDatabase { } impl Database for SqliteDatabase { - fn save(&self, h: History) -> Result<()> { + fn save(&mut self, h: History) -> Result<()> { debug!("saving history to sqlite"); + let v = vec![h]; - self.conn.execute( - "insert or ignore into history ( - id, - timestamp, - duration, - exit, - command, - cwd - ) values (?1, ?2, ?3, ?4, ?5, ?6)", - params![h.id, h.timestamp, h.duration, h.exit, h.command, h.cwd], - )?; - - Ok(()) + self.save_bulk(&v) } fn save_bulk(&mut self, h: &Vec<History>) -> Result<()> { @@ -95,9 +86,20 @@ impl Database for SqliteDatabase { duration, exit, command, - cwd - ) values (?1, ?2, ?3, ?4, ?5, ?6)", - params![i.id, i.timestamp, i.duration, i.exit, i.command, i.cwd], + cwd, + session, + hostname + ) values (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)", + params![ + i.id, + i.timestamp, + i.duration, + i.exit, + i.command, + i.cwd, + i.session, + i.hostname + ], )?; } @@ -110,7 +112,7 @@ impl Database for SqliteDatabase { debug!("loading history item"); let mut stmt = self.conn.prepare( - "select id, timestamp, duration, exit, command, cwd from history + "select id, timestamp, duration, exit, command, cwd, session, hostname from history where id = ?1", )?; @@ -122,6 +124,8 @@ impl Database for SqliteDatabase { exit: row.get(3)?, command: row.get(4)?, cwd: row.get(5)?, + session: row.get(6)?, + hostname: row.get(7)?, }) })?; @@ -137,9 +141,9 @@ impl Database for SqliteDatabase { self.conn.execute( "update history - set timestamp = ?2, duration = ?3, exit = ?4, command = ?5, cwd = ?6 + set timestamp = ?2, duration = ?3, exit = ?4, command = ?5, cwd = ?6, session = ?7, hostname = ?8 where id = ?1", - params![h.id, h.timestamp, h.duration, h.exit, h.command, h.cwd], + params![h.id, h.timestamp, h.duration, h.exit, h.command, h.cwd, h.session, h.hostname], )?; Ok(()) @@ -148,9 +152,9 @@ impl Database for SqliteDatabase { fn list(&self) -> Result<()> { debug!("listing history"); - let mut stmt = self - .conn - .prepare("SELECT id, timestamp, duration, exit, command, cwd FROM history")?; + let mut stmt = self.conn.prepare( + "SELECT id, timestamp, duration, exit, command, cwd, session, hostname FROM history", + )?; let history_iter = stmt.query_map(params![], |row| { Ok(History { @@ -160,6 +164,8 @@ impl Database for SqliteDatabase { exit: row.get(3)?, command: row.get(4)?, cwd: row.get(5)?, + session: row.get(6)?, + hostname: row.get(7)?, }) })?; @@ -167,8 +173,8 @@ impl Database for SqliteDatabase { let h = h.unwrap(); println!( - "{} | {} | {} | {} | {}", - h.timestamp, h.cwd, h.duration, h.exit, h.command + "{} | {} | {} | {} | {} | {} | {}", + h.timestamp, h.hostname, h.session, h.cwd, h.duration, h.exit, h.command ); } diff --git a/src/local/history.rs b/src/local/history.rs index 001096217..bb8b91234 100644 --- a/src/local/history.rs +++ b/src/local/history.rs @@ -1,4 +1,6 @@ -use chrono; +use std::env; + +use hostname; use uuid::Uuid; #[derive(Debug)] @@ -9,10 +11,32 @@ pub struct History { pub exit: i64, pub command: String, pub cwd: String, + pub session: String, + pub hostname: String, } impl History { - pub fn new(timestamp: i64, command: String, cwd: String, exit: i64, duration: i64) -> History { + pub fn new( + timestamp: i64, + command: String, + cwd: String, + exit: i64, + duration: i64, + session: Option<String>, + hostname: Option<String>, + ) -> History { + // get the current session or just generate a random string + let env_session = + env::var("ATUIN_SESSION").unwrap_or(Uuid::new_v4().to_simple().to_string()); + + // best attempt at getting the current hostname, or just unknown + let os_hostname = hostname::get().unwrap(); + let os_hostname = os_hostname.to_str().unwrap(); + let os_hostname = String::from(os_hostname); + + let session = session.unwrap_or(env_session); + let hostname = hostname.unwrap_or(os_hostname); + History { id: Uuid::new_v4().to_simple().to_string(), timestamp, @@ -20,6 +44,8 @@ impl History { cwd, exit, duration, + session, + hostname, } } } diff --git a/src/local/import.rs b/src/local/import.rs index ce141c52b..0b2378144 100644 --- a/src/local/import.rs +++ b/src/local/import.rs @@ -70,11 +70,13 @@ fn parse_extended(line: String) -> History { // use nanos, because why the hell not? we won't display them. History::new( - Utc.timestamp(time, 0).timestamp_nanos(), + time * 1_000_000_000, trim_newline(command), String::from("unknown"), -1, duration * 1_000_000_000, + None, + None, ) } @@ -104,6 +106,8 @@ impl Iterator for ImportZsh { String::from("unknown"), -1, -1, + None, + None, ))) } } diff --git a/src/main.rs b/src/main.rs index 19357cbe5..22c8b21df 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ use std::path::PathBuf; use directories::ProjectDirs; use eyre::{eyre, Result}; use structopt::StructOpt; +use uuid::Uuid; #[macro_use] extern crate log; @@ -40,8 +41,11 @@ enum AtuinCmd { #[structopt(about = "import shell history from file")] Import(ImportCmd), - #[structopt(about = "start a atuin server")] + #[structopt(about = "start an atuin server")] Server, + + #[structopt(about = "generates a UUID")] + Uuid, } impl Atuin { @@ -66,8 +70,12 @@ impl Atuin { let mut db = SqliteDatabase::new(db_path)?; match self.atuin { - AtuinCmd::History(history) => history.run(db), + AtuinCmd::History(history) => history.run(&mut db), AtuinCmd::Import(import) => import.run(&mut db), + AtuinCmd::Uuid => { + println!("{}", Uuid::new_v4().to_simple().to_string()); + Ok(()) + } _ => Ok(()), } } |