fix(import/zsh-histdb): missing or wrong fields (#1740)

* fix(import/zsh-histdb): import exit_status and session

* fix(import/zsh-histdb): avoid session id conflict

* fix(import/zsh-histdb): follow the format conventions of session and hostname

* fix(import/zsh-histdb): duration unit is nanosecond
This commit is contained in:
Aloxaf 2024-02-21 16:56:41 +08:00 committed by GitHub
parent 3c375cad07
commit 21fee97a62
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -20,8 +20,8 @@
// places.dir, // places.dir,
// commands.argv // commands.argv
// from history // from history
// left join commands on history.command_id = commands.rowid // left join commands on history.command_id = commands.id
// left join places on history.place_id = places.rowid ; // left join places on history.place_id = places.id ;
// //
// CREATE TABLE history (id integer primary key autoincrement, // CREATE TABLE history (id integer primary key autoincrement,
// session int, // session int,
@ -32,9 +32,12 @@
// duration int); // duration int);
// //
use std::collections::HashMap;
use std::env;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use async_trait::async_trait; use async_trait::async_trait;
use atuin_common::utils::uuid_v7;
use directories::UserDirs; use directories::UserDirs;
use eyre::{eyre, Result}; use eyre::{eyre, Result};
use sqlx::{sqlite::SqlitePool, Pool}; use sqlx::{sqlite::SqlitePool, Pool};
@ -57,39 +60,22 @@ pub struct HistDbEntry {
pub dir: Vec<u8>, pub dir: Vec<u8>,
pub argv: Vec<u8>, pub argv: Vec<u8>,
pub duration: i64, pub duration: i64,
pub exit_status: i64,
pub session: i64,
} }
impl From<HistDbEntry> for History { fn get_hostname() -> String {
fn from(histdb_item: HistDbEntry) -> Self { env::var("ATUIN_HOST_NAME").unwrap_or_else(|_| whoami::hostname())
let imported = History::import() }
.timestamp(histdb_item.start_time.assume_utc())
.command(
String::from_utf8(histdb_item.argv)
.unwrap_or_else(|_e| String::from(""))
.trim_end()
.to_string(),
)
.cwd(
String::from_utf8(histdb_item.dir)
.unwrap_or_else(|_e| String::from(""))
.trim_end()
.to_string(),
)
.duration(histdb_item.duration)
.hostname(
String::from_utf8(histdb_item.host)
.unwrap_or_else(|_e| String::from(""))
.trim_end()
.to_string(),
);
imported.build().into() fn get_username() -> String {
} env::var("ATUIN_HOST_USER").unwrap_or_else(|_| whoami::username())
} }
#[derive(Debug)] #[derive(Debug)]
pub struct ZshHistDb { pub struct ZshHistDb {
histdb: Vec<HistDbEntry>, histdb: Vec<HistDbEntry>,
username: String,
} }
/// Read db at given file, return vector of entries. /// Read db at given file, return vector of entries.
@ -99,7 +85,15 @@ async fn hist_from_db(dbpath: PathBuf) -> Result<Vec<HistDbEntry>> {
} }
async fn hist_from_db_conn(pool: Pool<sqlx::Sqlite>) -> Result<Vec<HistDbEntry>> { async fn hist_from_db_conn(pool: Pool<sqlx::Sqlite>) -> Result<Vec<HistDbEntry>> {
let query = "select history.id,history.start_time,history.duration,places.host,places.dir,commands.argv from history left join commands on history.command_id = commands.rowid left join places on history.place_id = places.rowid order by history.start_time"; let query = r#"
SELECT
history.id, history.start_time, history.duration, places.host, places.dir,
commands.argv, history.exit_status, history.session
FROM history
LEFT JOIN commands ON history.command_id = commands.id
LEFT JOIN places ON history.place_id = places.id
ORDER BY history.start_time
"#;
let histdb_vec: Vec<HistDbEntry> = sqlx::query_as::<_, HistDbEntry>(query) let histdb_vec: Vec<HistDbEntry> = sqlx::query_as::<_, HistDbEntry>(query)
.fetch_all(&pool) .fetch_all(&pool)
.await?; .await?;
@ -144,14 +138,42 @@ impl Importer for ZshHistDb {
let histdb_entry_vec = hist_from_db(dbpath).await?; let histdb_entry_vec = hist_from_db(dbpath).await?;
Ok(Self { Ok(Self {
histdb: histdb_entry_vec, histdb: histdb_entry_vec,
username: get_username(),
}) })
} }
async fn entries(&mut self) -> Result<usize> { async fn entries(&mut self) -> Result<usize> {
Ok(self.histdb.len()) Ok(self.histdb.len())
} }
async fn load(self, h: &mut impl Loader) -> Result<()> { async fn load(self, h: &mut impl Loader) -> Result<()> {
for i in self.histdb { let mut session_map = HashMap::new();
h.push(i.into()).await?; for entry in self.histdb {
let command = match std::str::from_utf8(&entry.argv) {
Ok(s) => s.trim_end(),
Err(_) => continue, // we can skip past things like invalid utf8
};
let cwd = match std::str::from_utf8(&entry.dir) {
Ok(s) => s.trim_end(),
Err(_) => continue, // we can skip past things like invalid utf8
};
let hostname = format!(
"{}:{}",
String::from_utf8(entry.host).unwrap_or_else(|_e| get_hostname()),
self.username
);
let session = session_map.entry(entry.session).or_insert_with(uuid_v7);
let imported = History::import()
.timestamp(entry.start_time.assume_utc())
.command(command)
.cwd(cwd)
.duration(entry.duration * 1_000_000_000)
.exit(entry.exit_status)
.session(session.as_simple().to_string())
.hostname(hostname)
.build();
h.push(imported.into()).await?;
} }
Ok(()) Ok(())
} }
@ -219,7 +241,10 @@ mod test {
// test histdb iterator // test histdb iterator
let histdb_vec = hist_from_db_conn(pool).await.unwrap(); let histdb_vec = hist_from_db_conn(pool).await.unwrap();
let histdb = ZshHistDb { histdb: histdb_vec }; let histdb = ZshHistDb {
histdb: histdb_vec,
username: get_username(),
};
println!("h: {:#?}", histdb.histdb); println!("h: {:#?}", histdb.histdb);
println!("counter: {:?}", histdb.histdb.len()); println!("counter: {:?}", histdb.histdb.len());