diff --git a/contrib/completions/_zoxide b/contrib/completions/_zoxide index fd898e6..6aac55d 100644 --- a/contrib/completions/_zoxide +++ b/contrib/completions/_zoxide @@ -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]' \ diff --git a/contrib/completions/zoxide.bash b/contrib/completions/zoxide.bash index 93c19d9..646a5c8 100644 --- a/contrib/completions/zoxide.bash +++ b/contrib/completions/zoxide.bash @@ -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 ;; *) diff --git a/contrib/completions/zoxide.fish b/contrib/completions/zoxide.fish index 276cbaf..bee4e0a 100644 --- a/contrib/completions/zoxide.fish +++ b/contrib/completions/zoxide.fish @@ -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' diff --git a/contrib/completions/zoxide.ts b/contrib/completions/zoxide.ts index 0200591..662410c 100644 --- a/contrib/completions/zoxide.ts +++ b/contrib/completions/zoxide.ts @@ -115,6 +115,7 @@ const completion: Fig.Spec = { suggestions: [ "autojump", "z", + "zoxide", ], }, }, diff --git a/src/cmd/cmd.rs b/src/cmd/cmd.rs index 5a78a83..3b07c3d 100644 --- a/src/cmd/cmd.rs +++ b/src/cmd/cmd.rs @@ -97,6 +97,7 @@ pub enum ImportFrom { Autojump, #[clap(alias = "fasd")] Z, + Zoxide, } /// Generate shell configuration diff --git a/src/cmd/import.rs b/src/cmd/import.rs index 182a25f..db35f53 100644 --- a/src/cmd/import.rs +++ b/src/cmd/import.rs @@ -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); + } } diff --git a/src/db/mod.rs b/src/db/mod.rs index 8eb9fc6..b6024ae 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -206,7 +206,7 @@ impl Database { .context("could not serialize database") } - fn deserialize(bytes: &[u8]) -> Result> { + pub(crate) fn deserialize(bytes: &[u8]) -> Result> { // 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