zoxide/src/main.rs

141 lines
4 KiB
Rust
Raw Normal View History

2020-03-05 13:09:32 +00:00
mod config;
mod db;
mod dir;
mod error;
mod types;
mod util;
2020-03-06 17:43:32 +00:00
use crate::config::get_zo_data;
2020-03-05 13:09:32 +00:00
use crate::db::DB;
use crate::error::AppError;
use crate::types::Timestamp;
2020-03-06 17:43:32 +00:00
use crate::util::{fzf_helper, get_current_time};
2020-03-05 13:09:32 +00:00
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;
2020-03-06 17:43:32 +00:00
fn zoxide_query(db: &mut DB, mut keywords: Vec<String>, now: Timestamp) -> Option<String> {
if let [path] = keywords.as_slice() {
if Path::new(path).is_dir() {
return Some(path.to_owned());
2020-03-05 13:09:32 +00:00
}
}
2020-03-06 17:43:32 +00:00
for keyword in &mut keywords {
keyword.make_ascii_lowercase();
}
if let Some(dir) = db.query(&keywords, now) {
return Some(dir.path);
2020-03-05 13:09:32 +00:00
}
2020-03-06 17:43:32 +00:00
None
2020-03-05 13:09:32 +00:00
}
fn zoxide_query_interactive(
db: &mut DB,
2020-03-06 17:43:32 +00:00
mut keywords: Vec<String>,
2020-03-05 13:09:32 +00:00
now: Timestamp,
) -> Result<Option<String>, failure::Error> {
db.remove_invalid();
2020-03-06 17:43:32 +00:00
for keyword in &mut keywords {
keyword.make_ascii_lowercase();
}
2020-03-05 13:09:32 +00:00
let dirs = db
.dirs
.iter()
2020-03-06 17:43:32 +00:00
.filter(|dir| dir.is_match(&keywords))
2020-03-05 13:09:32 +00:00
.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();
2020-03-06 17:43:32 +00:00
let db_path = get_zo_data()?;
2020-03-05 13:09:32 +00:00
let mut db = DB::open(&db_path)?;
if let Some(matches) = matches.subcommand_matches("query") {
let now = get_current_time()?;
2020-03-06 17:43:32 +00:00
let keywords = matches
.values_of_os("KEYWORD")
.unwrap_or_default()
.map(|keyword| match keyword.to_str() {
Some(keyword) => Ok(keyword.to_owned()),
None => Err(AppError::UnicodeError),
})
.collect::<Result<Vec<String>, _>>()?;
2020-03-05 13:09:32 +00:00
let path_opt = if matches.is_present("interactive") {
2020-03-06 17:43:32 +00:00
zoxide_query_interactive(&mut db, keywords, now)
2020-03-05 13:09:32 +00:00
} else {
2020-03-06 17:43:32 +00:00
Ok(zoxide_query(&mut db, keywords, now))
2020-03-05 13:09:32 +00:00
}?;
if let Some(path) = path_opt {
2020-03-07 17:23:13 +00:00
println!("query: {}", path.trim());
2020-03-05 13:09:32 +00:00
}
} 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);
}
}