zoxide/src/subcommand/import.rs

90 lines
2.4 KiB
Rust

use crate::db::{Db, Dir};
use crate::util::{get_db, path_to_str};
use anyhow::{bail, Context, Result};
use structopt::StructOpt;
use std::fs;
use std::path::{Path, PathBuf};
#[derive(Debug, StructOpt)]
#[structopt(about = "Import from z database")]
pub struct Import {
path: PathBuf,
#[structopt(long, help = "Merge entries into existing database")]
merge: bool,
}
impl Import {
pub fn run(&self) -> Result<()> {
import(&self.path, self.merge)
}
}
fn import<P: AsRef<Path>>(path: P, merge: bool) -> Result<()> {
let mut db = get_db()?;
if !db.dirs.is_empty() && !merge {
bail!(
"To prevent conflicts, you can only import from z with an empty zoxide database!\n\
If you wish to merge the two, specify the `--merge` flag."
);
}
let buffer = fs::read_to_string(&path)
.with_context(|| format!("could not read z database: {}", path.as_ref().display()))?;
for (idx, line) in buffer.lines().enumerate() {
if let Err(e) = import_line(&mut db, line) {
let line_num = idx + 1;
eprintln!("Error on line {}: {}", line_num, e);
}
}
db.modified = true;
println!("Completed import.");
Ok(())
}
fn import_line(db: &mut Db, line: &str) -> Result<()> {
let mut split_line = line.rsplitn(3, '|');
let (path_str, epoch_str, rank_str) = (|| {
let epoch_str = split_line.next()?;
let rank_str = split_line.next()?;
let path_str = split_line.next()?;
Some((path_str, epoch_str, rank_str))
})()
.context("invalid entry")?;
let epoch = epoch_str
.parse::<i64>()
.with_context(|| format!("invalid epoch: {}", epoch_str))?;
let rank = rank_str
.parse::<f64>()
.with_context(|| format!("invalid rank: {}", rank_str))?;
let path_abs = dunce::canonicalize(path_str)
.with_context(|| format!("could not resolve path: {}", path_str))?;
let path_abs_str = path_to_str(&path_abs)?;
// If the path exists in the database, add the ranks and set the epoch to
// the largest of the parsed epoch and the already present epoch.
if let Some(dir) = db.dirs.iter_mut().find(|dir| dir.path == path_abs_str) {
dir.rank += rank;
dir.last_accessed = epoch.max(dir.last_accessed);
} else {
db.dirs.push(Dir {
path: path_abs_str.to_string(),
rank,
last_accessed: epoch,
});
}
Ok(())
}