tidy some stuff (#6)
This commit is contained in:
parent
8af0034ae0
commit
72c5ea7914
6 changed files with 119 additions and 143 deletions
|
@ -34,7 +34,7 @@ pub enum Cmd {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_list(h: &Vec<History>) {
|
fn print_list(h: &[History]) {
|
||||||
for i in h {
|
for i in h {
|
||||||
println!("{}", i.command);
|
println!("{}", i.command);
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,7 @@ impl Cmd {
|
||||||
// print the ID
|
// print the ID
|
||||||
// we use this as the key for calling end
|
// we use this as the key for calling end
|
||||||
println!("{}", h.id);
|
println!("{}", h.id);
|
||||||
db.save(h)?;
|
db.save(&h)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ impl Cmd {
|
||||||
h.exit = *exit;
|
h.exit = *exit;
|
||||||
h.duration = chrono::Utc::now().timestamp_nanos() - h.timestamp;
|
h.duration = chrono::Utc::now().timestamp_nanos() - h.timestamp;
|
||||||
|
|
||||||
db.update(h)?;
|
db.update(&h)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,23 @@
|
||||||
pub mod history;
|
use structopt::StructOpt;
|
||||||
pub mod import;
|
|
||||||
pub mod server;
|
mod history;
|
||||||
|
mod import;
|
||||||
|
mod server;
|
||||||
|
|
||||||
|
#[derive(StructOpt)]
|
||||||
|
pub enum AtuinCmd {
|
||||||
|
#[structopt(
|
||||||
|
about="manipulate shell history",
|
||||||
|
aliases=&["h", "hi", "his", "hist", "histo", "histor"],
|
||||||
|
)]
|
||||||
|
History(history::Cmd),
|
||||||
|
|
||||||
|
#[structopt(about = "import shell history from file")]
|
||||||
|
Import(import::Cmd),
|
||||||
|
|
||||||
|
#[structopt(about = "start an atuin server")]
|
||||||
|
Server(server::Cmd),
|
||||||
|
|
||||||
|
#[structopt(about = "generates a UUID")]
|
||||||
|
Uuid,
|
||||||
|
}
|
||||||
|
|
|
@ -3,18 +3,18 @@ use std::path::Path;
|
||||||
|
|
||||||
use eyre::{eyre, Result};
|
use eyre::{eyre, Result};
|
||||||
|
|
||||||
use rusqlite::NO_PARAMS;
|
|
||||||
use rusqlite::{params, Connection};
|
use rusqlite::{params, Connection};
|
||||||
|
use rusqlite::{Transaction, NO_PARAMS};
|
||||||
|
|
||||||
use crate::History;
|
use super::history::History;
|
||||||
|
|
||||||
pub trait Database {
|
pub trait Database {
|
||||||
fn save(&mut self, h: History) -> Result<()>;
|
fn save(&mut self, h: &History) -> Result<()>;
|
||||||
fn save_bulk(&mut self, h: &[History]) -> Result<()>;
|
fn save_bulk(&mut self, h: &[History]) -> Result<()>;
|
||||||
fn load(&self, id: &str) -> Result<History>;
|
fn load(&self, id: &str) -> Result<History>;
|
||||||
fn list(&self) -> Result<Vec<History>>;
|
fn list(&self) -> Result<Vec<History>>;
|
||||||
fn since(&self, date: chrono::DateTime<Utc>) -> Result<Vec<History>>;
|
fn since(&self, date: chrono::DateTime<Utc>) -> Result<Vec<History>>;
|
||||||
fn update(&self, h: History) -> Result<()>;
|
fn update(&self, h: &History) -> Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Intended for use on a developer machine and not a sync server.
|
// Intended for use on a developer machine and not a sync server.
|
||||||
|
@ -65,46 +65,53 @@ impl Sqlite {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn save_raw(tx: &Transaction, h: &History) -> Result<()> {
|
||||||
|
tx.execute(
|
||||||
|
"insert or ignore into history (
|
||||||
|
id,
|
||||||
|
timestamp,
|
||||||
|
duration,
|
||||||
|
exit,
|
||||||
|
command,
|
||||||
|
cwd,
|
||||||
|
session,
|
||||||
|
hostname
|
||||||
|
) values (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)",
|
||||||
|
params![
|
||||||
|
h.id,
|
||||||
|
h.timestamp,
|
||||||
|
h.duration,
|
||||||
|
h.exit,
|
||||||
|
h.command,
|
||||||
|
h.cwd,
|
||||||
|
h.session,
|
||||||
|
h.hostname
|
||||||
|
],
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Database for Sqlite {
|
impl Database for Sqlite {
|
||||||
fn save(&mut self, h: History) -> Result<()> {
|
fn save(&mut self, h: &History) -> Result<()> {
|
||||||
debug!("saving history to sqlite");
|
debug!("saving history to sqlite");
|
||||||
let v = vec![h];
|
|
||||||
|
|
||||||
self.save_bulk(&v)
|
let tx = self.conn.transaction()?;
|
||||||
|
Self::save_raw(&tx, h)?;
|
||||||
|
tx.commit()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn save_bulk(&mut self, h: &[History]) -> Result<()> {
|
fn save_bulk(&mut self, h: &[History]) -> Result<()> {
|
||||||
debug!("saving history to sqlite");
|
debug!("saving history to sqlite");
|
||||||
|
|
||||||
let tx = self.conn.transaction()?;
|
let tx = self.conn.transaction()?;
|
||||||
|
|
||||||
for i in h {
|
for i in h {
|
||||||
tx.execute(
|
Self::save_raw(&tx, i)?
|
||||||
"insert or ignore into history (
|
|
||||||
id,
|
|
||||||
timestamp,
|
|
||||||
duration,
|
|
||||||
exit,
|
|
||||||
command,
|
|
||||||
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
|
|
||||||
],
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tx.commit()?;
|
tx.commit()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -119,16 +126,7 @@ impl Database for Sqlite {
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let mut iter = stmt.query_map(params![id], |row| {
|
let mut iter = stmt.query_map(params![id], |row| {
|
||||||
Ok(History {
|
history_from_sqlite_row(Some(id.to_string()), row)
|
||||||
id: String::from(id),
|
|
||||||
timestamp: row.get(1)?,
|
|
||||||
duration: row.get(2)?,
|
|
||||||
exit: row.get(3)?,
|
|
||||||
command: row.get(4)?,
|
|
||||||
cwd: row.get(5)?,
|
|
||||||
session: row.get(6)?,
|
|
||||||
hostname: row.get(7)?,
|
|
||||||
})
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let history = iter.next().unwrap();
|
let history = iter.next().unwrap();
|
||||||
|
@ -139,7 +137,7 @@ impl Database for Sqlite {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&self, h: History) -> Result<()> {
|
fn update(&self, h: &History) -> Result<()> {
|
||||||
debug!("updating sqlite history");
|
debug!("updating sqlite history");
|
||||||
|
|
||||||
self.conn.execute(
|
self.conn.execute(
|
||||||
|
@ -159,42 +157,43 @@ impl Database for Sqlite {
|
||||||
.conn
|
.conn
|
||||||
.prepare("SELECT * FROM history order by timestamp asc")?;
|
.prepare("SELECT * FROM history order by timestamp asc")?;
|
||||||
|
|
||||||
let history_iter = stmt.query_map(params![], |row| {
|
let history_iter = stmt.query_map(params![], |row| history_from_sqlite_row(None, row))?;
|
||||||
Ok(History {
|
|
||||||
id: row.get(0)?,
|
|
||||||
timestamp: row.get(1)?,
|
|
||||||
duration: row.get(2)?,
|
|
||||||
exit: row.get(3)?,
|
|
||||||
command: row.get(4)?,
|
|
||||||
cwd: row.get(5)?,
|
|
||||||
session: row.get(6)?,
|
|
||||||
hostname: row.get(7)?,
|
|
||||||
})
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(history_iter.filter_map(|x| x.ok()).collect())
|
Ok(history_iter.filter_map(Result::ok).collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn since(&self, date: chrono::DateTime<Utc>) -> Result<Vec<History>> {
|
fn since(&self, date: chrono::DateTime<Utc>) -> Result<Vec<History>> {
|
||||||
debug!("listing history");
|
debug!("listing history since {:?}", date);
|
||||||
|
|
||||||
let mut stmt = self.conn.prepare(
|
let mut stmt = self.conn.prepare(
|
||||||
"SELECT distinct command FROM history where timestamp > ?1 order by timestamp asc",
|
"SELECT distinct command FROM history where timestamp > ?1 order by timestamp asc",
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let history_iter = stmt.query_map(params![date.timestamp_nanos()], |row| {
|
let history_iter = stmt.query_map(params![date.timestamp_nanos()], |row| {
|
||||||
Ok(History {
|
history_from_sqlite_row(None, row)
|
||||||
id: row.get(0)?,
|
|
||||||
timestamp: row.get(1)?,
|
|
||||||
duration: row.get(2)?,
|
|
||||||
exit: row.get(3)?,
|
|
||||||
command: row.get(4)?,
|
|
||||||
cwd: row.get(5)?,
|
|
||||||
session: row.get(6)?,
|
|
||||||
hostname: row.get(7)?,
|
|
||||||
})
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(history_iter.filter_map(|x| x.ok()).collect())
|
Ok(history_iter.filter_map(Result::ok).collect())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn history_from_sqlite_row(
|
||||||
|
id: Option<String>,
|
||||||
|
row: &rusqlite::Row,
|
||||||
|
) -> Result<History, rusqlite::Error> {
|
||||||
|
let id = match id {
|
||||||
|
Some(id) => id,
|
||||||
|
None => row.get(0)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(History {
|
||||||
|
id,
|
||||||
|
timestamp: row.get(1)?,
|
||||||
|
duration: row.get(2)?,
|
||||||
|
exit: row.get(3)?,
|
||||||
|
command: row.get(4)?,
|
||||||
|
cwd: row.get(5)?,
|
||||||
|
session: row.get(6)?,
|
||||||
|
hostname: row.get(7)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -24,17 +24,11 @@ impl History {
|
||||||
session: Option<String>,
|
session: Option<String>,
|
||||||
hostname: Option<String>,
|
hostname: Option<String>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
// get the current session or just generate a random string
|
let session = session.unwrap_or_else(|| {
|
||||||
let env_session =
|
env::var("ATUIN_SESSION").unwrap_or_else(|_| Uuid::new_v4().to_simple().to_string())
|
||||||
env::var("ATUIN_SESSION").unwrap_or_else(|_| Uuid::new_v4().to_simple().to_string());
|
});
|
||||||
|
let hostname =
|
||||||
// best attempt at getting the current hostname, or just unknown
|
hostname.unwrap_or_else(|| hostname::get().unwrap().to_str().unwrap().to_string());
|
||||||
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);
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
id: Uuid::new_v4().to_simple().to_string(),
|
id: Uuid::new_v4().to_simple().to_string(),
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
// import old shell history!
|
// import old shell history!
|
||||||
// automatically hoover up all that we can find
|
// automatically hoover up all that we can find
|
||||||
|
|
||||||
use std::fs::File;
|
use std::io::{BufRead, BufReader, Seek, SeekFrom};
|
||||||
use std::io::{BufRead, BufReader};
|
use std::{fs::File, path::Path};
|
||||||
|
|
||||||
use eyre::{eyre, Result};
|
use eyre::{eyre, Result};
|
||||||
|
|
||||||
|
@ -16,19 +16,18 @@ pub struct Zsh {
|
||||||
}
|
}
|
||||||
|
|
||||||
// this could probably be sped up
|
// this could probably be sped up
|
||||||
fn count_lines(path: &str) -> Result<usize> {
|
fn count_lines(buf: &mut BufReader<File>) -> Result<usize> {
|
||||||
let file = File::open(path)?;
|
let lines = buf.lines().count();
|
||||||
let buf = BufReader::new(file);
|
buf.seek(SeekFrom::Start(0))?;
|
||||||
|
|
||||||
Ok(buf.lines().count())
|
Ok(lines)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Zsh {
|
impl Zsh {
|
||||||
pub fn new(path: &str) -> Result<Self> {
|
pub fn new(path: impl AsRef<Path>) -> Result<Self> {
|
||||||
let loc = count_lines(path)?;
|
|
||||||
|
|
||||||
let file = File::open(path)?;
|
let file = File::open(path)?;
|
||||||
let buf = BufReader::new(file);
|
let mut buf = BufReader::new(file);
|
||||||
|
let loc = count_lines(&mut buf)?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
file: buf,
|
file: buf,
|
||||||
|
@ -37,43 +36,25 @@ impl Zsh {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 parse_extended(line: &str) -> History {
|
fn parse_extended(line: &str) -> History {
|
||||||
let line = line.replacen(": ", "", 2);
|
let line = line.replacen(": ", "", 2);
|
||||||
let mut split = line.splitn(2, ':');
|
let (time, duration) = line.split_once(':').unwrap();
|
||||||
|
let (duration, command) = duration.split_once(';').unwrap();
|
||||||
|
|
||||||
let time = split.next().unwrap_or("-1");
|
let time = time.parse::<i64>().map_or_else(
|
||||||
let time = time
|
|_| chrono::Utc::now().timestamp_nanos(),
|
||||||
.parse::<i64>()
|
|t| t * 1_000_000_000,
|
||||||
.unwrap_or_else(|_| chrono::Utc::now().timestamp_nanos());
|
);
|
||||||
|
|
||||||
let duration_command = split.next().unwrap(); // might be 0;the command
|
let duration = duration.parse::<i64>().map_or(-1, |t| t * 1_000_000_000);
|
||||||
let mut split = duration_command.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.
|
// use nanos, because why the hell not? we won't display them.
|
||||||
History::new(
|
History::new(
|
||||||
time * 1_000_000_000,
|
time,
|
||||||
trim_newline(command),
|
command.trim_end().to_string(),
|
||||||
String::from("unknown"),
|
String::from("unknown"),
|
||||||
-1,
|
-1,
|
||||||
duration * 1_000_000_000,
|
duration,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
|
@ -101,7 +82,7 @@ impl Iterator for Zsh {
|
||||||
} else {
|
} else {
|
||||||
Some(Ok(History::new(
|
Some(Ok(History::new(
|
||||||
chrono::Utc::now().timestamp_nanos(), // what else? :/
|
chrono::Utc::now().timestamp_nanos(), // what else? :/
|
||||||
trim_newline(line.as_str()),
|
line.trim_end().to_string(),
|
||||||
String::from("unknown"),
|
String::from("unknown"),
|
||||||
-1,
|
-1,
|
||||||
-1,
|
-1,
|
||||||
|
|
22
src/main.rs
22
src/main.rs
|
@ -1,3 +1,4 @@
|
||||||
|
#![feature(str_split_once)]
|
||||||
#![feature(proc_macro_hygiene)]
|
#![feature(proc_macro_hygiene)]
|
||||||
#![feature(decl_macro)]
|
#![feature(decl_macro)]
|
||||||
#![warn(clippy::pedantic, clippy::nursery)]
|
#![warn(clippy::pedantic, clippy::nursery)]
|
||||||
|
@ -15,9 +16,8 @@ extern crate log;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate rocket;
|
extern crate rocket;
|
||||||
|
|
||||||
use command::{history, import, server};
|
use command::AtuinCmd;
|
||||||
use local::database::Sqlite;
|
use local::database::Sqlite;
|
||||||
use local::history::History;
|
|
||||||
|
|
||||||
mod command;
|
mod command;
|
||||||
mod local;
|
mod local;
|
||||||
|
@ -37,24 +37,6 @@ struct Atuin {
|
||||||
atuin: AtuinCmd,
|
atuin: AtuinCmd,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(StructOpt)]
|
|
||||||
enum AtuinCmd {
|
|
||||||
#[structopt(
|
|
||||||
about="manipulate shell history",
|
|
||||||
aliases=&["h", "hi", "his", "hist", "histo", "histor"],
|
|
||||||
)]
|
|
||||||
History(history::Cmd),
|
|
||||||
|
|
||||||
#[structopt(about = "import shell history from file")]
|
|
||||||
Import(import::Cmd),
|
|
||||||
|
|
||||||
#[structopt(about = "start an atuin server")]
|
|
||||||
Server(server::Cmd),
|
|
||||||
|
|
||||||
#[structopt(about = "generates a UUID")]
|
|
||||||
Uuid,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Atuin {
|
impl Atuin {
|
||||||
fn run(self) -> Result<()> {
|
fn run(self) -> Result<()> {
|
||||||
let db_path = if let Some(db_path) = self.db {
|
let db_path = if let Some(db_path) = self.db {
|
||||||
|
|
Loading…
Reference in a new issue