use std::fs::File; use std::io::Read; use std::path::PathBuf; use async_trait::async_trait; use eyre::{Result, bail}; use memchr::Memchr; use crate::history::History; pub mod bash; pub mod fish; pub mod nu; pub mod nu_histdb; pub mod powershell; pub mod replxx; pub mod resh; pub mod xonsh; pub mod xonsh_sqlite; pub mod zsh; pub mod zsh_histdb; #[async_trait] pub trait Importer: Sized { const NAME: &'static str; async fn new() -> Result; async fn entries(&mut self) -> Result; async fn load(self, loader: &mut impl Loader) -> Result<()>; } #[async_trait] pub trait Loader: Sync + Send { async fn push(&mut self, hist: History) -> eyre::Result<()>; } fn unix_byte_lines(input: &[u8]) -> impl Iterator { UnixByteLines { iter: memchr::memchr_iter(b'\n', input), bytes: input, i: 0, } } struct UnixByteLines<'a> { iter: Memchr<'a>, bytes: &'a [u8], i: usize, } impl<'a> Iterator for UnixByteLines<'a> { type Item = &'a [u8]; fn next(&mut self) -> Option { let j = self.iter.next()?; let out = &self.bytes[self.i..j]; self.i = j + 1; Some(out) } fn count(self) -> usize where Self: Sized, { self.iter.count() } } fn count_lines(input: &[u8]) -> usize { unix_byte_lines(input).count() } fn get_histpath(def: D) -> Result where D: FnOnce() -> Result, { if let Ok(p) = std::env::var("HISTFILE") { Ok(PathBuf::from(p)) } else { def() } } fn get_histfile_path(def: D) -> Result where D: FnOnce() -> Result, { get_histpath(def).and_then(is_file) } fn get_histdir_path(def: D) -> Result where D: FnOnce() -> Result, { get_histpath(def).and_then(is_dir) } fn read_to_end(path: PathBuf) -> Result> { let mut bytes = Vec::new(); let mut f = File::open(path)?; f.read_to_end(&mut bytes)?; Ok(bytes) } fn is_file(p: PathBuf) -> Result { if p.is_file() { Ok(p) } else { bail!( "Could not find history file {:?}. Try setting and exporting $HISTFILE", p ) } } fn is_dir(p: PathBuf) -> Result { if p.is_dir() { Ok(p) } else { bail!( "Could not find history directory {:?}. Try setting and exporting $HISTFILE", p ) } } #[cfg(test)] mod tests { use super::*; #[derive(Default)] pub struct TestLoader { pub buf: Vec, } #[async_trait] impl Loader for TestLoader { async fn push(&mut self, hist: History) -> Result<()> { self.buf.push(hist); Ok(()) } } }