summaryrefslogtreecommitdiffstats
path: root/src/local/import.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/local/import.rs')
-rw-r--r--src/local/import.rs118
1 files changed, 86 insertions, 32 deletions
diff --git a/src/local/import.rs b/src/local/import.rs
index 9bf79c72..d0f679c9 100644
--- a/src/local/import.rs
+++ b/src/local/import.rs
@@ -4,7 +4,9 @@
use std::io::{BufRead, BufReader, Seek, SeekFrom};
use std::{fs::File, path::Path};
-use eyre::{Result, WrapErr};
+use chrono::prelude::*;
+use chrono::Utc;
+use eyre::{eyre, Result};
use super::history::History;
@@ -13,6 +15,7 @@ pub struct Zsh {
file: BufReader<File>,
pub loc: u64,
+ pub counter: i64,
}
// this could probably be sped up
@@ -32,19 +35,23 @@ impl Zsh {
Ok(Self {
file: buf,
loc: loc as u64,
+ counter: 0,
})
}
}
-fn parse_extended(line: &str) -> History {
+fn parse_extended(line: &str, counter: i64) -> History {
let line = line.replacen(": ", "", 2);
let (time, duration) = line.split_once(':').unwrap();
let (duration, command) = duration.split_once(';').unwrap();
- let time = time.parse::<i64>().map_or_else(
- |_| chrono::Utc::now().timestamp_nanos(),
- |t| t * 1_000_000_000,
- );
+ let time = time
+ .parse::<i64>()
+ .unwrap_or_else(|_| chrono::Utc::now().timestamp());
+
+ let offset = chrono::Duration::milliseconds(counter);
+ let time = Utc.timestamp(time, 0);
+ let time = time + offset;
let duration = duration.parse::<i64>().map_or(-1, |t| t * 1_000_000_000);
@@ -60,6 +67,18 @@ fn parse_extended(line: &str) -> History {
)
}
+impl Zsh {
+ fn read_line(&mut self) -> Option<Result<String>> {
+ let mut line = String::new();
+
+ match self.file.read_line(&mut line) {
+ Ok(0) => None,
+ Ok(_) => Some(Ok(line)),
+ Err(e) => Some(Err(eyre!("failed to read line: {}", e))), // we can skip past things like invalid utf8
+ }
+ }
+}
+
impl Iterator for Zsh {
type Item = Result<History>;
@@ -68,54 +87,89 @@ impl Iterator for Zsh {
// These lines begin with :
// So, if the line begins with :, parse it. Otherwise it's just
// the command
- let mut line = String::new();
+ let line = self.read_line()?;
- match self.file.read_line(&mut line) {
- Ok(0) => None,
- Ok(_) => {
- let extended = line.starts_with(':');
-
- if extended {
- Some(Ok(parse_extended(line.as_str())))
- } else {
- Some(Ok(History::new(
- chrono::Utc::now().timestamp_nanos(), // what else? :/
- line.trim_end().to_string(),
- String::from("unknown"),
- -1,
- -1,
- None,
- None,
- )))
- }
+ if let Err(e) = line {
+ return Some(Err(e)); // :(
+ }
+
+ let mut line = line.unwrap();
+
+ while line.ends_with("\\\n") {
+ let next_line = self.read_line()?;
+
+ if next_line.is_err() {
+ // There's a chance that the last line of a command has invalid
+ // characters, the only safe thing to do is break :/
+ // usually just invalid utf8 or smth
+ // however, we really need to avoid missing history, so it's
+ // better to have some items that should have been part of
+ // something else, than to miss things. So break.
+ break;
}
- Err(e) => Some(Err(e).wrap_err("failed to parse line")),
+
+ line.push_str(next_line.unwrap().as_str());
+ }
+
+ // We have to handle the case where a line has escaped newlines.
+ // Keep reading until we have a non-escaped newline
+
+ let extended = line.starts_with(':');
+
+ if extended {
+ self.counter += 1;
+ Some(Ok(parse_extended(line.as_str(), self.counter)))
+ } else {
+ let time = chrono::Utc::now();
+ let offset = chrono::Duration::seconds(self.counter);
+ let time = time - offset;
+
+ self.counter += 1;
+
+ Some(Ok(History::new(
+ time,
+ line.trim_end().to_string(),
+ String::from("unknown"),
+ -1,
+ -1,
+ None,
+ None,
+ )))
}
}
}
#[cfg(test)]
mod test {
+ use chrono::prelude::*;
+ use chrono::Utc;
+
use super::parse_extended;
#[test]
fn test_parse_extended_simple() {
- let parsed = parse_extended(": 1613322469:0;cargo install atuin");
+ let parsed = parse_extended(": 1613322469:0;cargo install atuin", 0);
assert_eq!(parsed.command, "cargo install atuin");
assert_eq!(parsed.duration, 0);
- assert_eq!(parsed.timestamp, 1_613_322_469_000_000_000);
+ assert_eq!(parsed.timestamp, Utc.timestamp(1_613_322_469, 0));
- let parsed = parse_extended(": 1613322469:10;cargo install atuin;cargo update");
+ let parsed = parse_extended(": 1613322469:10;cargo install atuin;cargo update", 0);
assert_eq!(parsed.command, "cargo install atuin;cargo update");
assert_eq!(parsed.duration, 10_000_000_000);
- assert_eq!(parsed.timestamp, 1_613_322_469_000_000_000);
+ assert_eq!(parsed.timestamp, Utc.timestamp(1_613_322_469, 0));
- let parsed = parse_extended(": 1613322469:10;cargo :b̷i̶t̴r̵o̴t̴ ̵i̷s̴ ̷r̶e̵a̸l̷");
+ let parsed = parse_extended(": 1613322469:10;cargo :b̷i̶t̴r̵o̴t̴ ̵i̷s̴ ̷r̶e̵a̸l̷", 0);
assert_eq!(parsed.command, "cargo :b̷i̶t̴r̵o̴t̴ ̵i̷s̴ ̷r̶e̵a̸l̷");
assert_eq!(parsed.duration, 10_000_000_000);
- assert_eq!(parsed.timestamp, 1_613_322_469_000_000_000);
+ assert_eq!(parsed.timestamp, Utc.timestamp(1_613_322_469, 0));
+
+ let parsed = parse_extended(": 1613322469:10;cargo install \\n atuin\n", 0);
+
+ assert_eq!(parsed.command, "cargo install \\n atuin");
+ assert_eq!(parsed.duration, 10_000_000_000);
+ assert_eq!(parsed.timestamp, Utc.timestamp(1_613_322_469, 0));
}
}