From bb086808b1461a586ca249e312d5122c7da3c9c6 Mon Sep 17 00:00:00 2001 From: Jamie Quigley Date: Sat, 8 May 2021 17:29:46 +0100 Subject: Add importer for resh_history file (#69) * Added resh history importer * Silence trivial clippy warnings for the PR CI --- atuin-client/src/import/mod.rs | 1 + atuin-client/src/import/resh.rs | 56 ++++++++++++++++++++++++++++++ src/command/import.rs | 75 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 atuin-client/src/import/resh.rs diff --git a/atuin-client/src/import/mod.rs b/atuin-client/src/import/mod.rs index 3f8ea355..0b21d605 100644 --- a/atuin-client/src/import/mod.rs +++ b/atuin-client/src/import/mod.rs @@ -4,6 +4,7 @@ use std::io::{BufRead, BufReader, Seek, SeekFrom}; use eyre::Result; pub mod bash; +pub mod resh; pub mod zsh; // this could probably be sped up diff --git a/atuin-client/src/import/resh.rs b/atuin-client/src/import/resh.rs new file mode 100644 index 00000000..07f02036 --- /dev/null +++ b/atuin-client/src/import/resh.rs @@ -0,0 +1,56 @@ +use serde::Deserialize; + +#[derive(Deserialize, Debug)] +pub struct ReshEntry { + pub cmd_line: String, + pub exit_code: i64, + pub shell: String, + pub uname: String, + pub session_id: String, + pub home: String, + pub lang: String, + pub lc_all: String, + pub login: String, + pub pwd: String, + pub pwd_after: String, + pub shell_env: String, + pub term: String, + pub real_pwd: String, + pub real_pwd_after: String, + pub pid: i64, + pub session_pid: i64, + pub host: String, + pub hosttype: String, + pub ostype: String, + pub machtype: String, + pub shlvl: i64, + pub timezone_before: String, + pub timezone_after: String, + pub realtime_before: f64, + pub realtime_after: f64, + pub realtime_before_local: f64, + pub realtime_after_local: f64, + pub realtime_duration: f64, + pub realtime_since_session_start: f64, + pub realtime_since_boot: f64, + pub git_dir: String, + pub git_real_dir: String, + pub git_origin_remote: String, + pub git_dir_after: String, + pub git_real_dir_after: String, + pub git_origin_remote_after: String, + pub machine_id: String, + pub os_release_id: String, + pub os_release_version_id: String, + pub os_release_id_like: String, + pub os_release_name: String, + pub os_release_pretty_name: String, + pub resh_uuid: String, + pub resh_version: String, + pub resh_revision: String, + pub parts_merged: bool, + pub recalled: bool, + pub recall_last_cmd_line: String, + pub cols: String, + pub lines: String, +} diff --git a/src/command/import.rs b/src/command/import.rs index 09df5839..c51e918f 100644 --- a/src/command/import.rs +++ b/src/command/import.rs @@ -1,13 +1,15 @@ use std::env; use std::path::PathBuf; +use atuin_common::utils::uuid_v4; +use chrono::{TimeZone, Utc}; use directories::UserDirs; use eyre::{eyre, Result}; use structopt::StructOpt; -use atuin_client::database::Database; use atuin_client::history::History; use atuin_client::import::{bash::Bash, zsh::Zsh}; +use atuin_client::{database::Database, import::resh::ReshEntry}; use indicatif::ProgressBar; #[derive(StructOpt)] @@ -29,6 +31,12 @@ pub enum Cmd { aliases=&["b", "ba", "bas"], )] Bash, + + #[structopt( + about="import history from the resh history file", + aliases=&["r", "re", "res"], + )] + Resh, } impl Cmd { @@ -56,10 +64,75 @@ impl Cmd { Self::Zsh => import_zsh(db).await, Self::Bash => import_bash(db).await, + Self::Resh => import_resh(db).await, } } } +async fn import_resh(db: &mut (impl Database + Send + Sync)) -> Result<()> { + let histpath = std::path::Path::new(std::env::var("HOME")?.as_str()).join(".resh_history.json"); + + println!("Parsing .resh_history.json..."); + #[allow(clippy::filter_map)] + let history = std::fs::read_to_string(histpath)? + .split('\n') + .map(str::trim) + .map(|x| serde_json::from_str::(x)) + .filter_map(Result::ok) + .map(|x| { + #[allow(clippy::cast_possible_truncation)] + #[allow(clippy::cast_sign_loss)] + let timestamp = { + let secs = x.realtime_before.floor() as i64; + let nanosecs = (x.realtime_before.fract() * 1_000_000_000_f64).round() as u32; + Utc.timestamp(secs, nanosecs) + }; + #[allow(clippy::cast_possible_truncation)] + #[allow(clippy::cast_sign_loss)] + let duration = { + let secs = x.realtime_after.floor() as i64; + let nanosecs = (x.realtime_after.fract() * 1_000_000_000_f64).round() as u32; + let difference = Utc.timestamp(secs, nanosecs) - timestamp; + difference.num_nanoseconds().unwrap_or(0) + }; + + History { + id: uuid_v4(), + timestamp, + duration, + exit: x.exit_code, + command: x.cmd_line, + cwd: x.pwd, + session: uuid_v4(), + hostname: x.host, + } + }) + .collect::>(); + println!("Updating database..."); + + let progress = ProgressBar::new(history.len() as u64); + + let buf_size = 100; + let mut buf = Vec::<_>::with_capacity(buf_size); + + for i in history { + buf.push(i); + + if buf.len() == buf_size { + db.save_bulk(&buf).await?; + progress.inc(buf.len() as u64); + + buf.clear(); + } + } + + if !buf.is_empty() { + db.save_bulk(&buf).await?; + progress.inc(buf.len() as u64); + } + Ok(()) +} + async fn import_zsh(db: &mut (impl Database + Send + Sync)) -> Result<()> { // oh-my-zsh sets HISTFILE=~/.zhistory // zsh has no default value for this var, but uses ~/.zhistory. -- cgit v1.2.3