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