atuin_client/import/
mod.rs

1use std::fs::File;
2use std::io::Read;
3use std::path::PathBuf;
4
5use async_trait::async_trait;
6use eyre::{bail, Result};
7use memchr::Memchr;
8
9use crate::history::History;
10
11pub mod bash;
12pub mod fish;
13pub mod nu;
14pub mod nu_histdb;
15pub mod replxx;
16pub mod resh;
17pub mod xonsh;
18pub mod xonsh_sqlite;
19pub mod zsh;
20pub mod zsh_histdb;
21
22#[async_trait]
23pub trait Importer: Sized {
24    const NAME: &'static str;
25    async fn new() -> Result<Self>;
26    async fn entries(&mut self) -> Result<usize>;
27    async fn load(self, loader: &mut impl Loader) -> Result<()>;
28}
29
30#[async_trait]
31pub trait Loader: Sync + Send {
32    async fn push(&mut self, hist: History) -> eyre::Result<()>;
33}
34
35fn unix_byte_lines(input: &[u8]) -> impl Iterator<Item = &[u8]> {
36    UnixByteLines {
37        iter: memchr::memchr_iter(b'\n', input),
38        bytes: input,
39        i: 0,
40    }
41}
42
43struct UnixByteLines<'a> {
44    iter: Memchr<'a>,
45    bytes: &'a [u8],
46    i: usize,
47}
48
49impl<'a> Iterator for UnixByteLines<'a> {
50    type Item = &'a [u8];
51
52    fn next(&mut self) -> Option<Self::Item> {
53        let j = self.iter.next()?;
54        let out = &self.bytes[self.i..j];
55        self.i = j + 1;
56        Some(out)
57    }
58
59    fn count(self) -> usize
60    where
61        Self: Sized,
62    {
63        self.iter.count()
64    }
65}
66
67fn count_lines(input: &[u8]) -> usize {
68    unix_byte_lines(input).count()
69}
70
71fn get_histpath<D>(def: D) -> Result<PathBuf>
72where
73    D: FnOnce() -> Result<PathBuf>,
74{
75    if let Ok(p) = std::env::var("HISTFILE") {
76        is_file(PathBuf::from(p))
77    } else {
78        is_file(def()?)
79    }
80}
81
82fn read_to_end(path: PathBuf) -> Result<Vec<u8>> {
83    let mut bytes = Vec::new();
84    let mut f = File::open(path)?;
85    f.read_to_end(&mut bytes)?;
86    Ok(bytes)
87}
88fn is_file(p: PathBuf) -> Result<PathBuf> {
89    if p.is_file() {
90        Ok(p)
91    } else {
92        bail!("Could not find history file {:?}. Try setting $HISTFILE", p)
93    }
94}
95
96#[cfg(test)]
97mod tests {
98    use super::*;
99
100    #[derive(Default)]
101    pub struct TestLoader {
102        pub buf: Vec<History>,
103    }
104
105    #[async_trait]
106    impl Loader for TestLoader {
107        async fn push(&mut self, hist: History) -> Result<()> {
108            self.buf.push(hist);
109            Ok(())
110        }
111    }
112}