aboutsummaryrefslogtreecommitdiffstats
path: root/atuin-client/src/import
diff options
context:
space:
mode:
authorSteven Xu <stevenxxiu@users.noreply.github.com>2023-03-27 01:44:06 +1100
committerGitHub <noreply@github.com>2023-03-26 15:44:06 +0100
commita7cb21a51b393b436c0ca7e09c892ebb3c597ad0 (patch)
treed55d1ad07d5d01ccac6f5100bf4d131da748537c /atuin-client/src/import
parentAdd musl build (#809) (diff)
downloadatuin-a7cb21a51b393b436c0ca7e09c892ebb3c597ad0.zip
feat: add *Nushell* support (#788)
* feat: add *Nushell* support * refactor: use `sh` to swap `STDOUT` and `STDERR` instead of using a temporary file * feat: include both keybindings, with the current REPL buffer passed to *Atuin*'s * feat: don't record commands run by keybindings
Diffstat (limited to 'atuin-client/src/import')
-rw-r--r--atuin-client/src/import/mod.rs2
-rw-r--r--atuin-client/src/import/nu.rs76
-rw-r--r--atuin-client/src/import/nu_histdb.rs110
3 files changed, 188 insertions, 0 deletions
diff --git a/atuin-client/src/import/mod.rs b/atuin-client/src/import/mod.rs
index 65c4f41d..3d38cd29 100644
--- a/atuin-client/src/import/mod.rs
+++ b/atuin-client/src/import/mod.rs
@@ -8,6 +8,8 @@ use crate::history::History;
pub mod bash;
pub mod fish;
+pub mod nu;
+pub mod nu_histdb;
pub mod resh;
pub mod zsh;
pub mod zsh_histdb;
diff --git a/atuin-client/src/import/nu.rs b/atuin-client/src/import/nu.rs
new file mode 100644
index 00000000..0f107604
--- /dev/null
+++ b/atuin-client/src/import/nu.rs
@@ -0,0 +1,76 @@
+// import old shell history!
+// automatically hoover up all that we can find
+
+use std::{fs::File, io::Read, path::PathBuf};
+
+use async_trait::async_trait;
+use directories::BaseDirs;
+use eyre::{eyre, Result};
+
+use super::{unix_byte_lines, Importer, Loader};
+use crate::history::History;
+
+#[derive(Debug)]
+pub struct Nu {
+ bytes: Vec<u8>,
+}
+
+fn get_histpath() -> Result<PathBuf> {
+ let base = BaseDirs::new().ok_or_else(|| eyre!("could not determine data directory"))?;
+ let config_dir = base.config_dir().join("nushell");
+
+ let histpath = config_dir.join("history.txt");
+ if histpath.exists() {
+ Ok(histpath)
+ } else {
+ Err(eyre!("Could not find history file."))
+ }
+}
+
+#[async_trait]
+impl Importer for Nu {
+ const NAME: &'static str = "nu";
+
+ async fn new() -> Result<Self> {
+ let mut bytes = Vec::new();
+ let path = get_histpath()?;
+ let mut f = File::open(path)?;
+ f.read_to_end(&mut bytes)?;
+ Ok(Self { bytes })
+ }
+
+ async fn entries(&mut self) -> Result<usize> {
+ Ok(super::count_lines(&self.bytes))
+ }
+
+ async fn load(self, h: &mut impl Loader) -> Result<()> {
+ let now = chrono::Utc::now();
+
+ let mut counter = 0;
+ for b in unix_byte_lines(&self.bytes) {
+ let s = match std::str::from_utf8(b) {
+ Ok(s) => s,
+ Err(_) => continue, // we can skip past things like invalid utf8
+ };
+
+ let cmd: String = s.replace("<\\n>", "\n");
+
+ let offset = chrono::Duration::nanoseconds(counter);
+ counter += 1;
+
+ h.push(History::new(
+ now - offset, // preserve ordering
+ cmd,
+ String::from("unknown"),
+ -1,
+ -1,
+ None,
+ None,
+ None,
+ ))
+ .await?;
+ }
+
+ Ok(())
+ }
+}
diff --git a/atuin-client/src/import/nu_histdb.rs b/atuin-client/src/import/nu_histdb.rs
new file mode 100644
index 00000000..0fb5192e
--- /dev/null
+++ b/atuin-client/src/import/nu_histdb.rs
@@ -0,0 +1,110 @@
+// import old shell history!
+// automatically hoover up all that we can find
+
+use std::path::PathBuf;
+
+use async_trait::async_trait;
+use chrono::{prelude::*, Utc};
+use directories::BaseDirs;
+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 HistDbEntry {
+ pub id: i64,
+ pub command_line: Vec<u8>,
+ pub start_timestamp: i64,
+ pub session_id: i64,
+ pub hostname: Vec<u8>,
+ pub cwd: Vec<u8>,
+ pub duration_ms: i64,
+ pub exit_status: i64,
+ pub more_info: Vec<u8>,
+}
+
+impl From<HistDbEntry> for History {
+ fn from(histdb_item: HistDbEntry) -> Self {
+ let ts_secs = histdb_item.start_timestamp / 1000;
+ let ts_ns = (histdb_item.start_timestamp % 1000) * 1_000_000;
+ History::new(
+ DateTime::from_utc(NaiveDateTime::from_timestamp(ts_secs, ts_ns as u32), Utc),
+ String::from_utf8(histdb_item.command_line).unwrap(),
+ String::from_utf8(histdb_item.cwd).unwrap(),
+ histdb_item.exit_status,
+ histdb_item.duration_ms,
+ Some(format!("{:x}", histdb_item.session_id)),
+ Some(String::from_utf8(histdb_item.hostname).unwrap()),
+ None,
+ )
+ }
+}
+
+#[derive(Debug)]
+pub struct NuHistDb {
+ 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 = r#"
+ SELECT
+ id, command_line, start_timestamp, session_id, hostname, cwd, duration_ms, exit_status,
+ more_info
+ FROM history
+ ORDER BY start_timestamp
+ "#;
+ let histdb_vec: Vec<HistDbEntry> = sqlx::query_as::<_, HistDbEntry>(query)
+ .fetch_all(&pool)
+ .await?;
+ Ok(histdb_vec)
+}
+
+impl NuHistDb {
+ pub fn histpath() -> Result<PathBuf> {
+ let base = BaseDirs::new().ok_or_else(|| eyre!("could not determine data directory"))?;
+ let config_dir = base.config_dir().join("nushell");
+
+ let histdb_path = config_dir.join("history.sqlite3");
+ if histdb_path.exists() {
+ Ok(histdb_path)
+ } else {
+ Err(eyre!("Could not find history file."))
+ }
+ }
+}
+
+#[async_trait]
+impl Importer for NuHistDb {
+ // Not sure how this is used
+ const NAME: &'static str = "nu_histdb";
+
+ /// Creates a new NuHistDb and populates the history based on the pre-populated data
+ /// structure.
+ async fn new() -> Result<Self> {
+ let dbpath = NuHistDb::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(())
+ }
+}