diff --git a/Cargo.lock b/Cargo.lock index 8a25e3d5..96fb6df1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -144,6 +144,7 @@ dependencies = [ "reqwest", "rmp-serde", "rust-crypto", + "rustyline", "serde 1.0.126", "serde_derive", "serde_json", @@ -372,6 +373,17 @@ dependencies = [ "syn", ] +[[package]] +name = "clipboard-win" +version = "4.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4ea1881992efc993e4dc50a324cdbd03216e41bdc8385720ff47efc9bd2ca8" +dependencies = [ + "error-code", + "str-buf", + "winapi", +] + [[package]] name = "colored" version = "1.9.3" @@ -576,6 +588,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "endian-type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" + [[package]] name = "env_logger" version = "0.7.1" @@ -589,6 +607,16 @@ dependencies = [ "termcolor", ] +[[package]] +name = "error-code" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5115567ac25674e0043e472be13d14e537f37ea8aa4bdc4aef0c89add1db1ff" +dependencies = [ + "libc", + "str-buf", +] + [[package]] name = "eyre" version = "0.6.5" @@ -599,6 +627,17 @@ dependencies = [ "once_cell", ] +[[package]] +name = "fd-lock" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8806dd91a06a7a403a8e596f9bfbfb34e469efbc363fc9c9713e79e26472e36" +dependencies = [ + "cfg-if", + "libc", + "winapi", +] + [[package]] name = "fern" version = "0.6.0" @@ -1074,9 +1113,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.93" +version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41" +checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21" [[package]] name = "libsodium-sys" @@ -1153,6 +1192,15 @@ version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" +[[package]] +name = "memoffset" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" +dependencies = [ + "autocfg", +] + [[package]] name = "mime" version = "0.3.16" @@ -1215,6 +1263,28 @@ dependencies = [ "twoway", ] +[[package]] +name = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", +] + +[[package]] +name = "nix" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7555d6c7164cc913be1ce7f95cbecdabda61eb2ccd89008524af306fb7f5031" +dependencies = [ + "bitflags", + "cc", + "cfg-if", + "libc", + "memoffset", +] + [[package]] name = "nom" version = "5.1.2" @@ -1530,6 +1600,16 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8" +[[package]] +name = "radix_trie" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" +dependencies = [ + "endian-type", + "nibble_vec", +] + [[package]] name = "rand" version = "0.3.23" @@ -1831,6 +1911,30 @@ dependencies = [ "webpki", ] +[[package]] +name = "rustyline" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790487c3881a63489ae77126f57048b42d62d3b2bafbf37453ea19eedb6340d6" +dependencies = [ + "bitflags", + "cfg-if", + "clipboard-win", + "dirs-next", + "fd-lock", + "libc", + "log", + "memchr", + "nix", + "radix_trie", + "scopeguard", + "smallvec", + "unicode-segmentation", + "unicode-width", + "utf8parse", + "winapi", +] + [[package]] name = "ryu" version = "1.0.5" @@ -2128,6 +2232,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "str-buf" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d44a3643b4ff9caf57abcee9c2c621d6c03d9135e0d8b589bd9afb5992cb176a" + [[package]] name = "stringprep" version = "0.1.2" @@ -2562,6 +2672,12 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf8parse" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372" + [[package]] name = "uuid" version = "0.8.2" diff --git a/atuin-client/Cargo.toml b/atuin-client/Cargo.toml index 300ce26a..18a91d00 100644 --- a/atuin-client/Cargo.toml +++ b/atuin-client/Cargo.toml @@ -41,6 +41,7 @@ itertools = "0.10.0" shellexpand = "2" sqlx = { version = "0.5", features = [ "runtime-tokio-rustls", "uuid", "chrono", "sqlite" ] } minspan = "0.1.1" +rustyline = "9.0.0" [dev-dependencies] tokio-test = "*" diff --git a/atuin-client/src/import/bash.rs b/atuin-client/src/import/bash.rs index 1a171625..08366f46 100644 --- a/atuin-client/src/import/bash.rs +++ b/atuin-client/src/import/bash.rs @@ -42,7 +42,7 @@ impl Importer for Bash { Ok(home_dir.join(".bash_history")) } - fn parse(path: impl AsRef) -> Result { + fn parse(path: &impl AsRef) -> Result { Self::new(File::open(path)?) } } diff --git a/atuin-client/src/import/mod.rs b/atuin-client/src/import/mod.rs index d73d3857..135e1d19 100644 --- a/atuin-client/src/import/mod.rs +++ b/atuin-client/src/import/mod.rs @@ -8,6 +8,7 @@ use crate::history::History; pub mod bash; pub mod resh; pub mod zsh; +pub mod nu; // this could probably be sped up fn count_lines(buf: &mut BufReader) -> Result { @@ -20,5 +21,5 @@ fn count_lines(buf: &mut BufReader) -> Result { pub trait Importer: IntoIterator> + Sized { const NAME: &'static str; fn histpath() -> Result; - fn parse(path: impl AsRef) -> Result; + fn parse(path: &impl AsRef) -> Result; } diff --git a/atuin-client/src/import/nu.rs b/atuin-client/src/import/nu.rs new file mode 100644 index 00000000..87c7e19e --- /dev/null +++ b/atuin-client/src/import/nu.rs @@ -0,0 +1,70 @@ +// import old shell history! +// automatically hoover up all that we can find + +use std::{ + ops::Range, + path::{Path, PathBuf}, +}; + +use directories::ProjectDirs; +use eyre::{eyre, Result}; + +use super::Importer; +use crate::history::History; + +use rustyline::history; + +pub struct Nu { + hist: history::History, + iter: Range, +} + +impl Importer for Nu { + const NAME: &'static str = "nu"; + + fn histpath() -> Result { + // https://github.com/nushell/nushell/blob/55eafadf025fb8737ed922b2f471137e0cdfe1f9/crates/nu-data/src/config.rs#L197-L199 + let dir = ProjectDirs::from("org", "nushell", "nu") + .ok_or_else(|| eyre!("could not find nushell history path"))?; + let path = ProjectDirs::data_local_dir(&dir).to_owned(); + + Ok(path) + } + + fn parse(path: &impl AsRef) -> Result { + let mut hist = history::History::new(); + hist.load(path)?; + let len = hist.len(); + Ok(Self { + hist, + iter: 0..len, + }) + } +} + +impl Iterator for Nu { + type Item = Result; + + fn next(&mut self) -> Option { + let index = self.iter.next()?; + let item = self.hist.get(index)?; + + let time = chrono::Utc::now(); + let offset = chrono::Duration::seconds(index as i64); + let time = time - offset; + + Some(Ok(History::new( + time, + item.trim_end().to_string(), + String::from("unknown"), + -1, + -1, + None, + None, + ))) + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} diff --git a/atuin-client/src/import/resh.rs b/atuin-client/src/import/resh.rs index fa55300b..d67fc00f 100644 --- a/atuin-client/src/import/resh.rs +++ b/atuin-client/src/import/resh.rs @@ -86,7 +86,7 @@ impl Importer for Resh { Ok(home_dir.join(".resh_history.json")) } - fn parse(path: impl AsRef) -> Result { + fn parse(path: &impl AsRef) -> Result { let file = File::open(path)?; let mut buf = BufReader::new(file); let loc = count_lines(&mut buf)?; diff --git a/atuin-client/src/import/zsh.rs b/atuin-client/src/import/zsh.rs index b3db36b6..85bf8bb4 100644 --- a/atuin-client/src/import/zsh.rs +++ b/atuin-client/src/import/zsh.rs @@ -62,7 +62,7 @@ impl Importer for Zsh { } } - fn parse(path: impl AsRef) -> Result { + fn parse(path: &impl AsRef) -> Result { Self::new(File::open(path)?) } } diff --git a/src/command/import.rs b/src/command/import.rs index 53940abb..6ce38ec3 100644 --- a/src/command/import.rs +++ b/src/command/import.rs @@ -1,5 +1,6 @@ use std::{env, path::PathBuf}; +use atuin_client::import::nu::Nu; use eyre::{eyre, Result}; use structopt::StructOpt; @@ -33,6 +34,11 @@ pub enum Cmd { aliases=&["r", "re", "res"], )] Resh, + + #[structopt( + about="import history from the nu history file", + )] + Nu, } const BATCH_SIZE: usize = 100; @@ -63,6 +69,7 @@ impl Cmd { Self::Zsh => import::, _>(db, BATCH_SIZE).await, Self::Bash => import::, _>(db, BATCH_SIZE).await, Self::Resh => import::(db, BATCH_SIZE).await, + Self::Nu => import::(db, BATCH_SIZE).await, } } } @@ -77,7 +84,7 @@ where println!("Importing history from {}", I::NAME); let histpath = get_histpath::()?; - let contents = I::parse(histpath)?; + let contents = I::parse(&histpath)?; let iter = contents.into_iter(); let progress = if let (_, Some(upper_bound)) = iter.size_hint() {