atuin_client/import/
mod.rs1use 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}