add self-import to merge multiple zoxide databases

This commit is contained in:
Denis Cornehl 2022-04-30 19:11:09 +02:00
parent 1d64ae0b87
commit 4cbab9b812
7 changed files with 74 additions and 14 deletions

View File

@ -94,7 +94,7 @@ esac
;;
(import)
_arguments "${_arguments_options[@]}" \
'--from=[Application to import from]:FROM:(autojump z)' \
'--from=[Application to import from]:FROM:(autojump z zoxide)' \
'--merge[Merge into existing database]' \
'-h[Print help]' \
'--help[Print help]' \

View File

@ -154,7 +154,7 @@ _zoxide() {
fi
case "${prev}" in
--from)
COMPREPLY=($(compgen -W "autojump z" -- "${cur}"))
COMPREPLY=($(compgen -W "autojump z zoxide" -- "${cur}"))
return 0
;;
*)

View File

@ -22,7 +22,7 @@ complete -c zoxide -n "__fish_seen_subcommand_from edit; and __fish_seen_subcomm
complete -c zoxide -n "__fish_seen_subcommand_from edit; and __fish_seen_subcommand_from increment" -s V -l version -d 'Print version'
complete -c zoxide -n "__fish_seen_subcommand_from edit; and __fish_seen_subcommand_from reload" -s h -l help -d 'Print help'
complete -c zoxide -n "__fish_seen_subcommand_from edit; and __fish_seen_subcommand_from reload" -s V -l version -d 'Print version'
complete -c zoxide -n "__fish_seen_subcommand_from import" -l from -d 'Application to import from' -r -f -a "{autojump ,z }"
complete -c zoxide -n "__fish_seen_subcommand_from import" -l from -d 'Application to import from' -r -f -a "{autojump ,z ,zoxide }"
complete -c zoxide -n "__fish_seen_subcommand_from import" -l merge -d 'Merge into existing database'
complete -c zoxide -n "__fish_seen_subcommand_from import" -s h -l help -d 'Print help'
complete -c zoxide -n "__fish_seen_subcommand_from import" -s V -l version -d 'Print version'

View File

@ -115,6 +115,7 @@ const completion: Fig.Spec = {
suggestions: [
"autojump",
"z",
"zoxide",
],
},
},

View File

@ -97,6 +97,7 @@ pub enum ImportFrom {
Autojump,
#[clap(alias = "fasd")]
Z,
Zoxide,
}
/// Generate shell configuration

View File

@ -7,25 +7,41 @@ use crate::db::Database;
impl Run for Import {
fn run(&self) -> Result<()> {
let buffer = fs::read_to_string(&self.path).with_context(|| {
format!("could not open database for importing: {}", &self.path.display())
})?;
let mut db = Database::open()?;
if !self.merge && !db.dirs().is_empty() {
bail!("current database is not empty, specify --merge to continue anyway");
}
match self.from {
ImportFrom::Autojump => import_autojump(&mut db, &buffer),
ImportFrom::Z => import_z(&mut db, &buffer),
let buffer = fs::read(&self.path).with_context(|| {
format!("could not open database for importing: {}", &self.path.display())
})?;
if matches!(self.from, ImportFrom::Zoxide) {
from_self(&mut db, &buffer)?;
} else {
let buffer = std::str::from_utf8(&buffer).with_context(|| {
format!("could not open database for importing: {}", &self.path.display())
})?;
match self.from {
ImportFrom::Autojump => import_autojump(&mut db, buffer),
ImportFrom::Z => import_z(&mut db, buffer),
ImportFrom::Zoxide => unreachable!(),
}
.context("import error")?;
}
.context("import error")?;
db.save()
}
}
fn from_self(db: &mut Database, buffer: &[u8]) -> Result<()> {
for dir in Database::deserialize(buffer).context("could not deserialize database")? {
db.add_unchecked(dir.path, dir.rank, dir.last_accessed);
}
db.dedup();
Ok(())
}
fn import_autojump(db: &mut Database, buffer: &str) -> Result<()> {
for line in buffer.lines() {
if line.is_empty() {
@ -80,8 +96,10 @@ fn sigmoid(x: f64) -> f64 {
#[cfg(test)]
mod tests {
use super::*;
use crate::db::Dir;
use std::fs;
use super::{sigmoid, *};
use crate::db::{Database, Dir};
#[test]
fn from_autojump() {
@ -163,4 +181,44 @@ mod tests {
assert_eq!(dir1.last_accessed, dir2.last_accessed);
}
}
#[test]
fn test_from_self() {
let path = if cfg!(windows) { r"C:\foo\bar" } else { "/foo/bar" };
let second_path = if cfg!(windows) { r"C:\bar\foo" } else { "/bar/foo" };
let now = 946684800;
let before = 946684700;
let source_data_dir = tempfile::tempdir().unwrap();
{
let mut db = Database::open_dir(source_data_dir.path()).unwrap();
db.add(path, 1.0, now);
db.save().unwrap();
assert_eq!(db.dirs().len(), 1);
let dir = &db.dirs()[0];
assert_eq!(dir.path, path);
assert_eq!(dir.last_accessed, now);
assert_eq!(dir.rank, 1.0);
}
let dest_data_dir = tempfile::tempdir().unwrap();
let mut db = Database::open_dir(dest_data_dir.path()).unwrap();
db.add(path, 1.0, before);
db.add(second_path, 1.0, before);
db.save().unwrap();
let source_buf = fs::read(source_data_dir.path().join("db.zo")).unwrap();
super::from_self(&mut db, &source_buf).unwrap();
assert_eq!(db.dirs().len(), 2);
let dir = &db.dirs()[0];
assert_eq!(dir.path, second_path);
assert_eq!(dir.last_accessed, before);
assert_eq!(dir.rank, 1.0);
let dir2 = &db.dirs()[1];
assert_eq!(dir2.path, path);
assert_eq!(dir2.last_accessed, now);
assert_eq!(dir2.rank, 2.0);
}
}

View File

@ -206,7 +206,7 @@ impl Database {
.context("could not serialize database")
}
fn deserialize(bytes: &[u8]) -> Result<Vec<Dir>> {
pub(crate) fn deserialize(bytes: &[u8]) -> Result<Vec<Dir>> {
// Assume a maximum size for the database. This prevents bincode from throwing
// strange errors when it encounters invalid data.
const MAX_SIZE: u64 = 32 << 20; // 32 MiB