summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEllie Huxtable <e@elm.sh>2021-02-13 20:21:49 +0000
committerEllie Huxtable <e@elm.sh>2021-02-13 20:24:22 +0000
commit440c4fc2335d5286d14367e39c99bb4946efe9e3 (patch)
tree766f33c9c6b87998f22bc146fe7361e798b377df
parent099afe66ecfb569a8a04b66425ded29665e6a37c (diff)
Add sessions
-rw-r--r--Cargo.lock20
-rw-r--r--Cargo.toml3
-rw-r--r--README.md13
-rw-r--r--src/command/history.rs12
-rw-r--r--src/command/import.rs13
-rw-r--r--src/local/database.rs58
-rw-r--r--src/local/history.rs30
-rw-r--r--src/local/import.rs6
-rw-r--r--src/main.rs12
9 files changed, 130 insertions, 37 deletions
diff --git a/Cargo.lock b/Cargo.lock
index fcf1175d..bd8b3609 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 57117c29..c4b67a8c 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.*"
diff --git a/README.md b/README.md
index 030af679..6739500a 100644
--- a/README.md
+++ b/README.md
@@ -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 72f821c5..4caf4c17 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 5ece13a8..5ce0e749 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 2a4cc582..e9882fd7 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 00109621..bb8b9123 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 ce141c52..0b237814 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 19357cbe..22c8b21d 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(()),
}
}