atuin/src/command/import.rs

188 lines
4.8 KiB
Rust
Raw Normal View History

2021-02-13 12:37:00 -07:00
use std::env;
use std::path::PathBuf;
use directories::UserDirs;
2021-02-13 12:37:00 -07:00
use eyre::{eyre, Result};
use structopt::StructOpt;
use atuin_client::database::Database;
use atuin_client::history::History;
2021-04-26 04:50:31 -06:00
use atuin_client::import::{bash::Bash, zsh::Zsh};
2021-02-13 12:37:00 -07:00
use indicatif::ProgressBar;
#[derive(StructOpt)]
2021-02-14 08:15:26 -07:00
pub enum Cmd {
2021-02-13 12:37:00 -07:00
#[structopt(
about="import history for the current shell",
aliases=&["a", "au", "aut"],
)]
Auto,
#[structopt(
about="import history from the zsh history file",
aliases=&["z", "zs"],
)]
Zsh,
2021-04-26 04:50:31 -06:00
#[structopt(
about="import history from the bash history file",
aliases=&["b", "ba", "bas"],
)]
Bash,
2021-02-13 12:37:00 -07:00
}
2021-02-14 08:15:26 -07:00
impl Cmd {
pub async fn run(&self, db: &mut (impl Database + Send + Sync)) -> Result<()> {
2021-04-26 04:50:31 -06:00
println!(" Atuin ");
println!("======================");
println!(" \u{1f30d} ");
println!(" \u{1f418}\u{1f418}\u{1f418}\u{1f418} ");
println!(" \u{1f422} ");
println!("======================");
2021-02-14 08:15:26 -07:00
println!("Importing history...");
2021-02-13 12:37:00 -07:00
2021-02-14 08:15:26 -07:00
match self {
Self::Auto => {
let shell = env::var("SHELL").unwrap_or_else(|_| String::from("NO_SHELL"));
if shell.ends_with("/zsh") {
2021-02-14 08:15:26 -07:00
println!("Detected ZSH");
import_zsh(db).await
2021-02-14 08:15:26 -07:00
} else {
println!("cannot import {} history", shell);
Ok(())
}
2021-02-13 12:37:00 -07:00
}
Self::Zsh => import_zsh(db).await,
2021-04-26 04:50:31 -06:00
Self::Bash => import_bash(db).await,
2021-02-13 12:37:00 -07:00
}
2021-02-14 08:15:26 -07:00
}
}
2021-02-13 12:37:00 -07:00
async fn import_zsh(db: &mut (impl Database + Send + Sync)) -> Result<()> {
2021-02-14 08:15:26 -07:00
// oh-my-zsh sets HISTFILE=~/.zhistory
// zsh has no default value for this var, but uses ~/.zhistory.
// we could maybe be smarter about this in the future :)
2021-02-13 12:37:00 -07:00
2021-02-14 08:15:26 -07:00
let histpath = env::var("HISTFILE");
2021-02-13 12:37:00 -07:00
2021-02-14 08:15:26 -07:00
let histpath = if let Ok(p) = histpath {
let histpath = PathBuf::from(p);
2021-02-13 12:37:00 -07:00
if !histpath.exists() {
return Err(eyre!(
"Could not find history file {:?}. try updating $HISTFILE",
histpath
));
}
2021-02-14 08:15:26 -07:00
histpath
} else {
let user_dirs = UserDirs::new().unwrap();
let home_dir = user_dirs.home_dir();
let mut candidates = [".zhistory", ".zsh_history"].iter();
loop {
match candidates.next() {
Some(candidate) => {
let histpath = home_dir.join(candidate);
if histpath.exists() {
break histpath;
}
}
None => return Err(eyre!("Could not find history file. try setting $HISTFILE")),
}
}
};
2021-02-14 08:15:26 -07:00
let zsh = Zsh::new(histpath)?;
2021-02-13 12:37:00 -07:00
2021-02-14 08:15:26 -07:00
let progress = ProgressBar::new(zsh.loc);
2021-02-13 12:37:00 -07:00
2021-02-14 08:15:26 -07:00
let buf_size = 100;
let mut buf = Vec::<History>::with_capacity(buf_size);
for i in zsh
.filter_map(Result::ok)
.filter(|x| !x.command.trim().is_empty())
{
buf.push(i);
2021-02-13 12:37:00 -07:00
2021-02-14 08:15:26 -07:00
if buf.len() == buf_size {
db.save_bulk(&buf).await?;
2021-02-13 12:37:00 -07:00
progress.inc(buf.len() as u64);
buf.clear();
2021-02-14 08:15:26 -07:00
}
2021-02-13 12:37:00 -07:00
}
2021-02-14 08:15:26 -07:00
if !buf.is_empty() {
db.save_bulk(&buf).await?;
2021-02-14 08:15:26 -07:00
progress.inc(buf.len() as u64);
}
2021-02-13 13:21:49 -07:00
2021-02-15 13:31:58 -07:00
progress.finish();
println!("Import complete!");
2021-02-13 12:37:00 -07:00
2021-02-14 08:15:26 -07:00
Ok(())
2021-02-13 12:37:00 -07:00
}
2021-04-26 04:50:31 -06:00
// TODO: don't just copy paste this lol
async fn import_bash(db: &mut (impl Database + Send + Sync)) -> Result<()> {
// oh-my-zsh sets HISTFILE=~/.zhistory
// zsh has no default value for this var, but uses ~/.zhistory.
// we could maybe be smarter about this in the future :)
let histpath = env::var("HISTFILE");
let histpath = if let Ok(p) = histpath {
let histpath = PathBuf::from(p);
if !histpath.exists() {
return Err(eyre!(
"Could not find history file {:?}. try updating $HISTFILE",
histpath
));
}
histpath
} else {
let user_dirs = UserDirs::new().unwrap();
let home_dir = user_dirs.home_dir();
home_dir.join(".bash_history")
};
let bash = Bash::new(histpath)?;
let progress = ProgressBar::new(bash.loc);
let buf_size = 100;
let mut buf = Vec::<History>::with_capacity(buf_size);
for i in bash
.filter_map(Result::ok)
.filter(|x| !x.command.trim().is_empty())
{
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);
}
progress.finish();
println!("Import complete!");
Ok(())
}