aboutsummaryrefslogtreecommitdiffstats
path: root/src/local
diff options
context:
space:
mode:
Diffstat (limited to 'src/local')
-rw-r--r--src/local/database.rs31
-rw-r--r--src/local/history.rs4
-rw-r--r--src/local/import.rs101
-rw-r--r--src/local/mod.rs1
4 files changed, 118 insertions, 19 deletions
diff --git a/src/local/database.rs b/src/local/database.rs
index b2c009b6..2a4cc582 100644
--- a/src/local/database.rs
+++ b/src/local/database.rs
@@ -9,6 +9,7 @@ use crate::History;
pub trait Database {
fn save(&self, h: History) -> Result<()>;
+ fn save_bulk(&mut self, h: &Vec<History>) -> Result<()>;
fn load(&self, id: &str) -> Result<History>;
fn list(&self) -> Result<()>;
fn update(&self, h: History) -> Result<()>;
@@ -51,7 +52,9 @@ impl SqliteDatabase {
duration integer not null,
exit integer not null,
command text not null,
- cwd text not null
+ cwd text not null,
+
+ unique(timestamp, cwd, command)
)",
NO_PARAMS,
)?;
@@ -65,7 +68,7 @@ impl Database for SqliteDatabase {
debug!("saving history to sqlite");
self.conn.execute(
- "insert into history (
+ "insert or ignore into history (
id,
timestamp,
duration,
@@ -79,6 +82,30 @@ impl Database for SqliteDatabase {
Ok(())
}
+ fn save_bulk(&mut self, h: &Vec<History>) -> Result<()> {
+ debug!("saving history to sqlite");
+
+ let tx = self.conn.transaction()?;
+
+ for i in h {
+ tx.execute(
+ "insert or ignore into history (
+ id,
+ timestamp,
+ duration,
+ exit,
+ command,
+ cwd
+ ) values (?1, ?2, ?3, ?4, ?5, ?6)",
+ params![i.id, i.timestamp, i.duration, i.exit, i.command, i.cwd],
+ )?;
+ }
+
+ tx.commit()?;
+
+ Ok(())
+ }
+
fn load(&self, id: &str) -> Result<History> {
debug!("loading history item");
diff --git a/src/local/history.rs b/src/local/history.rs
index 3c9a9069..00109621 100644
--- a/src/local/history.rs
+++ b/src/local/history.rs
@@ -12,10 +12,10 @@ pub struct History {
}
impl History {
- pub fn new(command: String, cwd: String, exit: i64, duration: i64) -> History {
+ pub fn new(timestamp: i64, command: String, cwd: String, exit: i64, duration: i64) -> History {
History {
id: Uuid::new_v4().to_simple().to_string(),
- timestamp: chrono::Utc::now().timestamp_millis(),
+ timestamp,
command,
cwd,
exit,
diff --git a/src/local/import.rs b/src/local/import.rs
index 8db8f0e3..ce141c52 100644
--- a/src/local/import.rs
+++ b/src/local/import.rs
@@ -4,38 +4,109 @@
use std::fs::File;
use std::io::{BufRead, BufReader};
-use eyre::Result;
+use chrono::{TimeZone, Utc};
+use eyre::{eyre, Result};
-use crate::models::history::History;
+use crate::local::history::History;
-pub struct ImportBash {
+#[derive(Debug)]
+pub struct ImportZsh {
file: BufReader<File>,
+
+ pub loc: u64,
}
-impl ImportBash {
- pub fn new(path: &str) -> Result<ImportBash> {
+// this could probably be sped up
+fn count_lines(path: &str) -> Result<usize> {
+ let file = File::open(path)?;
+ let buf = BufReader::new(file);
+
+ Ok(buf.lines().count())
+}
+
+impl ImportZsh {
+ pub fn new(path: &str) -> Result<ImportZsh> {
+ let loc = count_lines(path)?;
+
let file = File::open(path)?;
let buf = BufReader::new(file);
- Ok(ImportBash { file: buf })
+ Ok(ImportZsh {
+ file: buf,
+ loc: loc as u64,
+ })
}
}
-impl Iterator for ImportBash {
- type Item = History;
+fn trim_newline(s: &str) -> String {
+ let mut s = String::from(s);
+
+ if s.ends_with('\n') {
+ s.pop();
+ if s.ends_with('\r') {
+ s.pop();
+ }
+ }
+
+ s
+}
- fn next(&mut self) -> Option<History> {
+fn parse_extended(line: String) -> History {
+ let line = line.replacen(": ", "", 2);
+ let mut split = line.splitn(2, ":");
+
+ let time = split.next().unwrap_or("-1");
+ let time = time
+ .parse::<i64>()
+ .unwrap_or(chrono::Utc::now().timestamp_nanos());
+
+ let duration = split.next().unwrap(); // might be 0;the command
+ let mut split = duration.split(";");
+
+ let duration = split.next().unwrap_or("-1"); // should just be the 0
+ let duration = duration.parse::<i64>().unwrap_or(-1);
+
+ let command = split.next().unwrap();
+
+ // use nanos, because why the hell not? we won't display them.
+ History::new(
+ Utc.timestamp(time, 0).timestamp_nanos(),
+ trim_newline(command),
+ String::from("unknown"),
+ -1,
+ duration * 1_000_000_000,
+ )
+}
+
+impl Iterator for ImportZsh {
+ type Item = Result<History>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ // ZSH extended history records the timestamp + command duration
+ // These lines begin with :
+ // So, if the line begins with :, parse it. Otherwise it's just
+ // the command
let mut line = String::new();
match self.file.read_line(&mut line) {
Ok(0) => None,
- Err(_) => None,
+ Err(e) => Some(Err(eyre!("failed to parse line: {}", e))),
+
+ Ok(_) => {
+ let extended = line.starts_with(":");
- Ok(_) => Some(History {
- cwd: "none".to_string(),
- command: line,
- timestamp: -1,
- }),
+ if extended {
+ Some(Ok(parse_extended(line)))
+ } else {
+ Some(Ok(History::new(
+ chrono::Utc::now().timestamp_nanos(), // what else? :/
+ trim_newline(line.as_str()),
+ String::from("unknown"),
+ -1,
+ -1,
+ )))
+ }
+ }
}
}
}
diff --git a/src/local/mod.rs b/src/local/mod.rs
index f587d016..a11ee213 100644
--- a/src/local/mod.rs
+++ b/src/local/mod.rs
@@ -1,2 +1,3 @@
pub mod database;
pub mod history;
+pub mod import;