-
-
Couldn't load subscription status.
- Fork 716
Noyez zsh histdb import #393
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
ad36770
Attempting to implement zsh-histdb import
noyez c545a08
More working on importing histdb
noyez 49791ca
Merge remote-tracking branch 'upstream/main' into noyez_zsh_histdb_im…
noyez 5c52392
Rewriting tests and using `Vec<u8>` instead of `String`
noyez 47e3c47
Running fmt
noyez File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,219 @@ | ||
| // import old shell history from zsh-histdb! | ||
| // automatically hoover up all that we can find | ||
|
|
||
| // As far as i can tell there are no version numbers in the histdb sqlite DB, so we're going based | ||
| // on the schema from 2022-05-01 | ||
| // | ||
| // I have run into some histories that will not import b/c of non UTF-8 characters. | ||
| // | ||
|
|
||
| // | ||
| // An Example sqlite query for hsitdb data: | ||
| // | ||
| //id|session|command_id|place_id|exit_status|start_time|duration|id|argv|id|host|dir | ||
| // | ||
| // | ||
| // select | ||
| // history.id, | ||
| // history.start_time, | ||
| // 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 ; | ||
| // | ||
| // CREATE TABLE history (id integer primary key autoincrement, | ||
| // session int, | ||
| // command_id int references commands (id), | ||
| // place_id int references places (id), | ||
| // exit_status int, | ||
| // start_time int, | ||
| // duration int); | ||
| // | ||
|
|
||
| use std::path::{Path, PathBuf}; | ||
|
|
||
| use async_trait::async_trait; | ||
| use chrono::{prelude::*, Utc}; | ||
| use directories::UserDirs; | ||
| use eyre::{eyre, Result}; | ||
| use sqlx::{sqlite::SqlitePool, Pool}; | ||
|
|
||
| use super::Importer; | ||
| use crate::history::History; | ||
| use crate::import::Loader; | ||
|
|
||
| #[derive(sqlx::FromRow, Debug)] | ||
| pub struct HistDbEntryCount { | ||
| pub count: usize, | ||
| } | ||
|
|
||
| #[derive(sqlx::FromRow, Debug)] | ||
| pub struct HistDbEntry { | ||
| pub id: i64, | ||
| pub start_time: NaiveDateTime, | ||
| pub host: String, | ||
| pub dir: String, | ||
| pub argv: Vec<u8>, | ||
| pub duration: i64, | ||
| } | ||
|
|
||
| impl From<HistDbEntry> for History { | ||
| fn from(histdb_item: HistDbEntry) -> Self { | ||
| History::new( | ||
| DateTime::from_utc(histdb_item.start_time, Utc), // must assume UTC? | ||
| String::from_utf8(histdb_item.argv) | ||
| .unwrap_or_else(|_e| String::from("")) | ||
| .trim_end() | ||
| .to_string(), | ||
| histdb_item.dir, | ||
| 0, // assume 0, we have no way of knowing :( | ||
| histdb_item.duration, | ||
| None, | ||
| Some(histdb_item.host), | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| #[derive(Debug)] | ||
| pub struct ZshHistDb { | ||
| histdb: Vec<HistDbEntry>, | ||
| } | ||
|
|
||
| /// Read db at given file, return vector of entries. | ||
| async fn hist_from_db(dbpath: PathBuf) -> Result<Vec<HistDbEntry>> { | ||
| let pool = SqlitePool::connect(dbpath.to_str().unwrap()).await?; | ||
| hist_from_db_conn(pool).await | ||
| } | ||
|
|
||
| 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 histdb_vec: Vec<HistDbEntry> = sqlx::query_as::<_, HistDbEntry>(query) | ||
| .fetch_all(&pool) | ||
| .await?; | ||
| Ok(histdb_vec) | ||
| } | ||
|
|
||
| impl ZshHistDb { | ||
| pub fn histpath_candidate() -> PathBuf { | ||
| // By default histdb database is `${HOME}/.histdb/zsh-history.db` | ||
| // This can be modified by ${HISTDB_FILE} | ||
| // | ||
| // if [[ -z ${HISTDB_FILE} ]]; then | ||
| // typeset -g HISTDB_FILE="${HOME}/.histdb/zsh-history.db" | ||
| let user_dirs = UserDirs::new().unwrap(); // should catch error here? | ||
| let home_dir = user_dirs.home_dir(); | ||
| std::env::var("HISTDB_FILE") | ||
| .as_ref() | ||
| .map(|x| Path::new(x).to_path_buf()) | ||
| .unwrap_or_else(|_err| home_dir.join(".histdb/zsh-history.db")) | ||
| } | ||
| pub fn histpath() -> Result<PathBuf> { | ||
| let histdb_path = ZshHistDb::histpath_candidate(); | ||
| if histdb_path.exists() { | ||
| Ok(histdb_path) | ||
| } else { | ||
| Err(eyre!( | ||
| "Could not find history file. Try setting $HISTDB_FILE" | ||
| )) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| #[async_trait] | ||
| impl Importer for ZshHistDb { | ||
| // Not sure how this is used | ||
| const NAME: &'static str = "zsh_histdb"; | ||
|
|
||
| /// Creates a new ZshHistDb and populates the history based on the pre-populated data | ||
| /// structure. | ||
| async fn new() -> Result<Self> { | ||
| let dbpath = ZshHistDb::histpath()?; | ||
| let histdb_entry_vec = hist_from_db(dbpath).await?; | ||
| Ok(Self { | ||
| histdb: histdb_entry_vec, | ||
| }) | ||
| } | ||
| async fn entries(&mut self) -> Result<usize> { | ||
| Ok(self.histdb.len()) | ||
| } | ||
| async fn load(self, h: &mut impl Loader) -> Result<()> { | ||
| for i in self.histdb { | ||
| h.push(i.into()).await?; | ||
| } | ||
| Ok(()) | ||
| } | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod test { | ||
|
|
||
| use super::*; | ||
| use sqlx::sqlite::SqlitePoolOptions; | ||
| use std::env; | ||
| #[tokio::test(flavor = "multi_thread")] | ||
| async fn test_env_vars() { | ||
| let test_env_db = "nonstd-zsh-history.db"; | ||
| let key = "HISTDB_FILE"; | ||
| env::set_var(key, test_env_db); | ||
|
|
||
| // test the env got set | ||
| assert_eq!(env::var(key).unwrap(), test_env_db.to_string()); | ||
|
|
||
| // test histdb returns the proper db from previous step | ||
| let histdb_path = ZshHistDb::histpath_candidate(); | ||
| assert_eq!(histdb_path.to_str().unwrap(), test_env_db); | ||
| } | ||
|
|
||
| #[tokio::test(flavor = "multi_thread")] | ||
| async fn test_import() { | ||
| let pool: SqlitePool = SqlitePoolOptions::new() | ||
| .min_connections(2) | ||
| .connect(":memory:") | ||
| .await | ||
| .unwrap(); | ||
|
|
||
| // sql dump directly from a test database. | ||
| let db_sql = r#" | ||
| PRAGMA foreign_keys=OFF; | ||
| BEGIN TRANSACTION; | ||
| CREATE TABLE commands (id integer primary key autoincrement, argv text, unique(argv) on conflict ignore); | ||
| INSERT INTO commands VALUES(1,'pwd'); | ||
| INSERT INTO commands VALUES(2,'curl google.com'); | ||
| INSERT INTO commands VALUES(3,'bash'); | ||
| CREATE TABLE places (id integer primary key autoincrement, host text, dir text, unique(host, dir) on conflict ignore); | ||
| INSERT INTO places VALUES(1,'mbp16.local','/home/noyez'); | ||
| CREATE TABLE history (id integer primary key autoincrement, | ||
| session int, | ||
| command_id int references commands (id), | ||
| place_id int references places (id), | ||
| exit_status int, | ||
| start_time int, | ||
| duration int); | ||
| INSERT INTO history VALUES(1,0,1,1,0,1651497918,1); | ||
| INSERT INTO history VALUES(2,0,2,1,0,1651497923,1); | ||
| INSERT INTO history VALUES(3,0,3,1,NULL,1651497930,NULL); | ||
| DELETE FROM sqlite_sequence; | ||
| INSERT INTO sqlite_sequence VALUES('commands',3); | ||
| INSERT INTO sqlite_sequence VALUES('places',3); | ||
| INSERT INTO sqlite_sequence VALUES('history',3); | ||
| CREATE INDEX hist_time on history(start_time); | ||
| CREATE INDEX place_dir on places(dir); | ||
| CREATE INDEX place_host on places(host); | ||
| CREATE INDEX history_command_place on history(command_id, place_id); | ||
| COMMIT; "#; | ||
|
|
||
| sqlx::query(db_sql).execute(&pool).await.unwrap(); | ||
|
|
||
| // test histdb iterator | ||
| let histdb_vec = hist_from_db_conn(pool).await.unwrap(); | ||
| let histdb = ZshHistDb { histdb: histdb_vec }; | ||
|
|
||
| println!("h: {:#?}", histdb.histdb); | ||
| println!("counter: {:?}", histdb.histdb.len()); | ||
| for i in histdb.histdb { | ||
| println!("{:?}", i); | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is needed anymore