mirror of
https://github.com/Llewellynvdm/zoxide.git
synced 2024-11-22 12:55:13 +00:00
Avoid use of hashmap for imports
This commit is contained in:
parent
9ef2ef5a6d
commit
fcdfb19fd1
@ -62,8 +62,8 @@ _arguments "${_arguments_options[@]}" \
|
|||||||
'(-l --list)--interactive[Use interactive selection]' \
|
'(-l --list)--interactive[Use interactive selection]' \
|
||||||
'(-i --interactive)-l[List all matching directories]' \
|
'(-i --interactive)-l[List all matching directories]' \
|
||||||
'(-i --interactive)--list[List all matching directories]' \
|
'(-i --interactive)--list[List all matching directories]' \
|
||||||
'-s[Print score with results]' \
|
'(-i --interactive)-s[Print score with results]' \
|
||||||
'--score[Print score with results]' \
|
'(-i --interactive)--score[Print score with results]' \
|
||||||
'-h[Prints help information]' \
|
'-h[Prints help information]' \
|
||||||
'--help[Prints help information]' \
|
'--help[Prints help information]' \
|
||||||
'*::keywords:' \
|
'*::keywords:' \
|
||||||
|
@ -114,7 +114,7 @@ pub struct Query {
|
|||||||
pub list: bool,
|
pub list: bool,
|
||||||
|
|
||||||
/// Print score with results
|
/// Print score with results
|
||||||
#[clap(long, short)]
|
#[clap(long, short, conflicts_with = "interactive")]
|
||||||
pub score: bool,
|
pub score: bool,
|
||||||
|
|
||||||
/// Exclude a path from results
|
/// Exclude a path from results
|
||||||
|
@ -1,17 +1,18 @@
|
|||||||
use crate::app::{Import, ImportFrom, Run};
|
use crate::app::{Import, ImportFrom, Run};
|
||||||
use crate::config;
|
use crate::config;
|
||||||
use crate::db::{Database, DatabaseFile, Dir, DirList};
|
use crate::db::{Database, DatabaseFile, Dir};
|
||||||
|
|
||||||
use anyhow::{bail, Context, Result};
|
use anyhow::{bail, Context, Result};
|
||||||
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
impl Run for Import {
|
impl Run for Import {
|
||||||
fn run(&self) -> Result<()> {
|
fn run(&self) -> Result<()> {
|
||||||
let data_dir = config::data_dir()?;
|
let buffer = &fs::read_to_string(&self.path).with_context(|| {
|
||||||
|
format!("could not open database for importing: {}", &self.path.display())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let data_dir = config::data_dir()?;
|
||||||
let mut db = DatabaseFile::new(data_dir);
|
let mut db = DatabaseFile::new(data_dir);
|
||||||
let db = &mut db.open()?;
|
let db = &mut db.open()?;
|
||||||
if !self.merge && !db.dirs.is_empty() {
|
if !self.merge && !db.dirs.is_empty() {
|
||||||
@ -19,21 +20,16 @@ impl Run for Import {
|
|||||||
}
|
}
|
||||||
|
|
||||||
match self.from {
|
match self.from {
|
||||||
ImportFrom::Autojump => from_autojump(db, &self.path),
|
ImportFrom::Autojump => from_autojump(db, buffer),
|
||||||
ImportFrom::Z => from_z(db, &self.path),
|
ImportFrom::Z => from_z(db, buffer),
|
||||||
}
|
}
|
||||||
.context("import error")
|
.context("import error")?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_autojump<P: AsRef<Path>>(db: &mut Database, path: P) -> Result<()> {
|
fn from_autojump<'a>(db: &mut Database<'a>, buffer: &'a str) -> Result<()> {
|
||||||
let path = path.as_ref();
|
|
||||||
let buffer = fs::read_to_string(path)
|
|
||||||
.with_context(|| format!("could not open autojump database: {}", path.display()))?;
|
|
||||||
|
|
||||||
let mut dirs =
|
|
||||||
db.dirs.iter().map(|dir| (dir.path.as_ref(), dir.clone())).collect::<HashMap<_, _>>();
|
|
||||||
|
|
||||||
for line in buffer.lines() {
|
for line in buffer.lines() {
|
||||||
if line.is_empty() {
|
if line.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
@ -45,31 +41,22 @@ fn from_autojump<P: AsRef<Path>>(db: &mut Database, path: P) -> Result<()> {
|
|||||||
// Normalize the rank using a sigmoid function. Don't import actual
|
// Normalize the rank using a sigmoid function. Don't import actual
|
||||||
// ranks from autojump, since its scoring algorithm is very different,
|
// ranks from autojump, since its scoring algorithm is very different,
|
||||||
// and might take a while to get normalized.
|
// and might take a while to get normalized.
|
||||||
rank = 1.0 / (1.0 + (-rank).exp());
|
rank = sigmoid(rank);
|
||||||
|
|
||||||
let path = split.next().with_context(|| format!("invalid entry: {}", line))?;
|
let path = split.next().with_context(|| format!("invalid entry: {}", line))?;
|
||||||
|
|
||||||
dirs.entry(path).and_modify(|dir| dir.rank += rank).or_insert_with(|| Dir {
|
db.dirs.push(Dir { path: path.into(), rank, last_accessed: 0 });
|
||||||
path: path.to_string().into(),
|
db.modified = true;
|
||||||
rank,
|
|
||||||
last_accessed: 0,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
db.dirs = DirList(dirs.into_iter().map(|(_, dir)| dir).collect());
|
if db.modified {
|
||||||
db.modified = true;
|
db.dedup();
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_z<P: AsRef<Path>>(db: &mut Database, path: P) -> Result<()> {
|
fn from_z<'a>(db: &mut Database<'a>, buffer: &'a str) -> Result<()> {
|
||||||
let path = path.as_ref();
|
|
||||||
let buffer = fs::read_to_string(path)
|
|
||||||
.with_context(|| format!("could not open z database: {}", path.display()))?;
|
|
||||||
|
|
||||||
let mut dirs =
|
|
||||||
db.dirs.iter().map(|dir| (dir.path.as_ref(), dir.clone())).collect::<HashMap<_, _>>();
|
|
||||||
|
|
||||||
for line in buffer.lines() {
|
for line in buffer.lines() {
|
||||||
if line.is_empty() {
|
if line.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
@ -85,18 +72,104 @@ fn from_z<P: AsRef<Path>>(db: &mut Database, path: P) -> Result<()> {
|
|||||||
|
|
||||||
let path = split.next().with_context(|| format!("invalid entry: {}", line))?;
|
let path = split.next().with_context(|| format!("invalid entry: {}", line))?;
|
||||||
|
|
||||||
dirs.entry(path)
|
db.dirs.push(Dir { path: path.into(), rank, last_accessed });
|
||||||
.and_modify(|dir| {
|
db.modified = true;
|
||||||
dir.rank += rank;
|
|
||||||
if last_accessed > dir.last_accessed {
|
|
||||||
dir.last_accessed = last_accessed;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.or_insert(Dir { path: path.to_string().into(), rank, last_accessed });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
db.dirs = DirList(dirs.into_iter().map(|(_, dir)| dir).collect());
|
if db.modified {
|
||||||
db.modified = true;
|
db.dedup();
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn sigmoid(x: f64) -> f64 {
|
||||||
|
1.0 / (1.0 + (-x).exp())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::sigmoid;
|
||||||
|
use crate::db::{Database, Dir};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn from_autojump() {
|
||||||
|
let buffer = r#"
|
||||||
|
7.0 /baz
|
||||||
|
2.0 /foo/bar
|
||||||
|
5.0 /quux/quuz
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let dirs = vec![
|
||||||
|
Dir { path: "/quux/quuz".into(), rank: 1.0, last_accessed: 100 },
|
||||||
|
Dir { path: "/corge/grault/garply".into(), rank: 6.0, last_accessed: 600 },
|
||||||
|
Dir { path: "/waldo/fred/plugh".into(), rank: 3.0, last_accessed: 300 },
|
||||||
|
Dir { path: "/xyzzy/thud".into(), rank: 8.0, last_accessed: 800 },
|
||||||
|
Dir { path: "/foo/bar".into(), rank: 9.0, last_accessed: 900 },
|
||||||
|
];
|
||||||
|
let data_dir = tempfile::tempdir().unwrap();
|
||||||
|
let data_dir = &data_dir.path().to_path_buf();
|
||||||
|
let mut db = Database { dirs: dirs.into(), modified: false, data_dir };
|
||||||
|
|
||||||
|
super::from_autojump(&mut db, buffer).unwrap();
|
||||||
|
db.dirs.sort_by(|dir1, dir2| dir1.path.cmp(&dir2.path));
|
||||||
|
println!("got: {:?}", &db.dirs.as_slice());
|
||||||
|
|
||||||
|
let exp = &[
|
||||||
|
Dir { path: "/baz".into(), rank: sigmoid(7.0), last_accessed: 0 },
|
||||||
|
Dir { path: "/corge/grault/garply".into(), rank: 6.0, last_accessed: 600 },
|
||||||
|
Dir { path: "/foo/bar".into(), rank: 9.0 + sigmoid(2.0), last_accessed: 900 },
|
||||||
|
Dir { path: "/quux/quuz".into(), rank: 1.0 + sigmoid(5.0), last_accessed: 100 },
|
||||||
|
Dir { path: "/waldo/fred/plugh".into(), rank: 3.0, last_accessed: 300 },
|
||||||
|
Dir { path: "/xyzzy/thud".into(), rank: 8.0, last_accessed: 800 },
|
||||||
|
];
|
||||||
|
println!("exp: {:?}", &exp);
|
||||||
|
|
||||||
|
for (dir1, dir2) in db.dirs.iter().zip(exp) {
|
||||||
|
assert_eq!(dir1.path, dir2.path);
|
||||||
|
assert!((dir1.rank - dir2.rank).abs() < 0.01);
|
||||||
|
assert_eq!(dir1.last_accessed, dir2.last_accessed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn from_z() {
|
||||||
|
let buffer = r#"
|
||||||
|
/baz|7|700
|
||||||
|
/quux/quuz|4|400
|
||||||
|
/foo/bar|2|200
|
||||||
|
/quux/quuz|5|500
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let dirs = vec![
|
||||||
|
Dir { path: "/quux/quuz".into(), rank: 1.0, last_accessed: 100 },
|
||||||
|
Dir { path: "/corge/grault/garply".into(), rank: 6.0, last_accessed: 600 },
|
||||||
|
Dir { path: "/waldo/fred/plugh".into(), rank: 3.0, last_accessed: 300 },
|
||||||
|
Dir { path: "/xyzzy/thud".into(), rank: 8.0, last_accessed: 800 },
|
||||||
|
Dir { path: "/foo/bar".into(), rank: 9.0, last_accessed: 900 },
|
||||||
|
];
|
||||||
|
let data_dir = tempfile::tempdir().unwrap();
|
||||||
|
let data_dir = &data_dir.path().to_path_buf();
|
||||||
|
let mut db = Database { dirs: dirs.into(), modified: false, data_dir };
|
||||||
|
|
||||||
|
super::from_z(&mut db, buffer).unwrap();
|
||||||
|
db.dirs.sort_by(|dir1, dir2| dir1.path.cmp(&dir2.path));
|
||||||
|
println!("got: {:?}", &db.dirs.as_slice());
|
||||||
|
|
||||||
|
let exp = &[
|
||||||
|
Dir { path: "/baz".into(), rank: 7.0, last_accessed: 700 },
|
||||||
|
Dir { path: "/corge/grault/garply".into(), rank: 6.0, last_accessed: 600 },
|
||||||
|
Dir { path: "/foo/bar".into(), rank: 11.0, last_accessed: 900 },
|
||||||
|
Dir { path: "/quux/quuz".into(), rank: 10.0, last_accessed: 500 },
|
||||||
|
Dir { path: "/waldo/fred/plugh".into(), rank: 3.0, last_accessed: 300 },
|
||||||
|
Dir { path: "/xyzzy/thud".into(), rank: 8.0, last_accessed: 800 },
|
||||||
|
];
|
||||||
|
println!("exp: {:?}", &exp);
|
||||||
|
|
||||||
|
for (dir1, dir2) in db.dirs.iter().zip(exp) {
|
||||||
|
assert_eq!(dir1.path, dir2.path);
|
||||||
|
assert!((dir1.rank - dir2.rank).abs() < 0.01);
|
||||||
|
assert_eq!(dir1.last_accessed, dir2.last_accessed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -11,10 +11,11 @@ use std::fs;
|
|||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Database<'file> {
|
pub struct Database<'file> {
|
||||||
pub dirs: DirList<'file>,
|
pub dirs: DirList<'file>,
|
||||||
pub modified: bool,
|
pub modified: bool,
|
||||||
data_dir: &'file PathBuf,
|
pub data_dir: &'file PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'file> Database<'file> {
|
impl<'file> Database<'file> {
|
||||||
@ -61,6 +62,33 @@ impl<'file> Database<'file> {
|
|||||||
self.modified = true;
|
self.modified = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn dedup(&mut self) {
|
||||||
|
// Sort by path, so that equal paths are next to each other.
|
||||||
|
self.dirs.sort_by(|dir1, dir2| dir1.path.cmp(&dir2.path));
|
||||||
|
|
||||||
|
for idx in (1..self.dirs.len()).rev() {
|
||||||
|
// Check if curr_dir and next_dir have equal paths.
|
||||||
|
let curr_dir = &self.dirs[idx];
|
||||||
|
let next_dir = &self.dirs[idx - 1];
|
||||||
|
if next_dir.path != curr_dir.path {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge curr_dir's rank and last_accessed into next_dir.
|
||||||
|
let rank = curr_dir.rank;
|
||||||
|
let last_accessed = curr_dir.last_accessed;
|
||||||
|
let next_dir = &mut self.dirs[idx - 1];
|
||||||
|
if next_dir.last_accessed < last_accessed {
|
||||||
|
next_dir.last_accessed = last_accessed;
|
||||||
|
}
|
||||||
|
next_dir.rank += rank;
|
||||||
|
|
||||||
|
// Delete curr_dir.
|
||||||
|
self.dirs.swap_remove(idx);
|
||||||
|
self.modified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Streaming iterator for directories.
|
// Streaming iterator for directories.
|
||||||
pub fn stream(&mut self, now: Epoch) -> Stream<'_, 'file> {
|
pub fn stream(&mut self, now: Epoch) -> Stream<'_, 'file> {
|
||||||
Stream::new(self, now)
|
Stream::new(self, now)
|
||||||
|
Loading…
Reference in New Issue
Block a user