128 lines
3.6 KiB
Rust
128 lines
3.6 KiB
Rust
mod config;
|
|
mod db;
|
|
mod dir;
|
|
mod error;
|
|
mod types;
|
|
mod util;
|
|
|
|
use crate::db::DB;
|
|
use crate::error::AppError;
|
|
use crate::types::Timestamp;
|
|
use crate::util::{fzf_helper, get_current_time, get_db_path, process_query};
|
|
use clap::{app_from_crate, crate_authors, crate_description, crate_name, crate_version};
|
|
use clap::{App, Arg, SubCommand};
|
|
use failure::ResultExt;
|
|
use std::env;
|
|
use std::path::Path;
|
|
|
|
fn zoxide_query(
|
|
db: &mut DB,
|
|
keywords: &[String],
|
|
now: Timestamp,
|
|
) -> Result<Option<String>, failure::Error> {
|
|
if let [path] = keywords {
|
|
if Path::new(&path).is_dir() {
|
|
return Ok(Some(path.to_owned()));
|
|
}
|
|
}
|
|
|
|
if let Some(dir) = db.query(keywords, now) {
|
|
return Ok(Some(dir.path));
|
|
}
|
|
|
|
Ok(None)
|
|
}
|
|
|
|
fn zoxide_query_interactive(
|
|
db: &mut DB,
|
|
keywords: &[String],
|
|
now: Timestamp,
|
|
) -> Result<Option<String>, failure::Error> {
|
|
db.remove_invalid();
|
|
|
|
let dirs = db
|
|
.dirs
|
|
.iter()
|
|
.filter(|dir| dir.is_match(keywords))
|
|
.cloned()
|
|
.collect();
|
|
|
|
fzf_helper(now, dirs)
|
|
}
|
|
|
|
fn zoxide_app() -> App<'static, 'static> {
|
|
app_from_crate!()
|
|
.subcommand(
|
|
SubCommand::with_name("add")
|
|
.about("Add a new directory or increment its rank")
|
|
.author(crate_authors!())
|
|
.version(crate_version!())
|
|
.arg(Arg::with_name("PATH")),
|
|
)
|
|
.subcommand(
|
|
SubCommand::with_name("query")
|
|
.about("Search for a directory")
|
|
.author(crate_authors!())
|
|
.version(crate_version!())
|
|
.arg(
|
|
Arg::with_name("interactive")
|
|
.short("i")
|
|
.long("interactive")
|
|
.takes_value(false)
|
|
.help("Opens an interactive selection menu using fzf"),
|
|
)
|
|
.arg(Arg::with_name("KEYWORD").min_values(0)),
|
|
)
|
|
.subcommand(
|
|
SubCommand::with_name("remove")
|
|
.about("Remove a directory")
|
|
.author(crate_authors!())
|
|
.version(crate_version!())
|
|
.arg(Arg::with_name("PATH").required(true)),
|
|
)
|
|
}
|
|
|
|
fn zoxide() -> Result<(), failure::Error> {
|
|
let matches = zoxide_app().get_matches();
|
|
|
|
let db_path = get_db_path()?;
|
|
let mut db = DB::open(&db_path)?;
|
|
|
|
if let Some(matches) = matches.subcommand_matches("query") {
|
|
let now = get_current_time()?;
|
|
let keywords = process_query(matches.values_of("KEYWORD").unwrap_or_default());
|
|
|
|
let path_opt = if matches.is_present("interactive") {
|
|
zoxide_query_interactive(&mut db, &keywords, now)
|
|
} else {
|
|
zoxide_query(&mut db, &keywords, now)
|
|
}?;
|
|
|
|
if let Some(path) = path_opt {
|
|
print!("query: {}", path);
|
|
}
|
|
} else if let Some(matches) = matches.subcommand_matches("add") {
|
|
let now = get_current_time()?;
|
|
match matches.value_of_os("PATH") {
|
|
Some(path) => db.add(path, now)?,
|
|
None => {
|
|
let path = env::current_dir().with_context(|_| AppError::GetCurrentDirError)?;
|
|
db.add(path, now)?;
|
|
}
|
|
};
|
|
} else if let Some(matches) = matches.subcommand_matches("remove") {
|
|
// unwrap is safe here because PATH has been set as a required field
|
|
let path = matches.value_of_os("PATH").unwrap();
|
|
db.remove(path)?;
|
|
}
|
|
|
|
db.save(db_path)
|
|
}
|
|
|
|
fn main() {
|
|
if let Err(err) = zoxide() {
|
|
eprintln!("zoxide: {}", err);
|
|
std::process::exit(1);
|
|
}
|
|
}
|