diar 2.3.0

A directory favorite tool in rust
Documentation
extern crate clap;
extern crate sled;

use clap::ArgMatches;
use clap::{App, Arg, SubCommand};
use diar::command::{to_command, Command};
use diar::types::{JumpTo, WhereToAdd};
use diar::util::generate_path_string;
use sled::Db;
use std::fs;
use std::path::Path;
use colored::Colorize;

mod add;
mod clear;
mod delete;
mod jump;
mod list;
mod ls;
mod rename;

fn main() {
    let users_db_path = generate_path_string("/.diar".to_owned());
    let db_path = Path::new(&users_db_path);
    rename_diar_directory();
    let db = Db::open(&db_path).unwrap();
    let app = App::new("diar")
        .version("2.3.0")
        .author("Index197511 and 4afS")
        .about("A directory favorite tool in Rust.")
        .subcommand(
            SubCommand::with_name("add")
                .about("Add a directory you like to favorite")
                .arg(
                    Arg::with_name("path")
                        .help("absolute path")
                        .short("p")
                        .long("path")
                        .takes_value(true),
                )
                .arg(
                    Arg::with_name("key")
                        .help("key to directory")
                        .takes_value(true)
                        .required(true),
                ),
        )
        .subcommand(
            SubCommand::with_name("delete")
                .about("Delete a directory from favorite")
                .arg(
                    Arg::with_name("key")
                        .help("named directory")
                        .takes_value(true)
                        .required(true),
                ),
        )
        .subcommand(
            SubCommand::with_name("rename")
                .about("Rename favorite directory")
                .arg(
                    Arg::with_name("old_key")
                        .help("old key to favorite directory")
                        .takes_value(true)
                        .required(true),
                )
                .arg(
                    Arg::with_name("new_key")
                        .help("new key to favorite directory")
                        .takes_value(true)
                        .required(true),
                ),
        )
        .subcommand(SubCommand::with_name("list").about("List favorite directories"))
        .subcommand(
            SubCommand::with_name("jump")
                .about("Jump to your favorite directory or root directory of the project")
                .arg(
                    Arg::with_name("key")
                        .help("favorite dirs key")
                        .takes_value(true),
                )
                .arg(
                    Arg::with_name("project-root")
                        .help("The current project root directory")
                        .long("project-root")
                        .short("p"),
                )
                .arg(
                    Arg::with_name("fuzzy-finder")
                        .help("fuzzy finder")
                        .short("f")
                    )
        )
        .subcommand(SubCommand::with_name("clear").about("Delete all favorite directories."))
        .subcommand(
            SubCommand::with_name("ls")
                .about("ls your favorite directory")
                .arg(
                    Arg::with_name("key")
                        .help("favorite dir's key")
                        .takes_value(true)
                        .required(true),
                ),
        );

    let matches = app.get_matches();

    match matches.subcommand_name() {
        Some(subcommand_name) => match to_command(subcommand_name) {
            Some(Command::Add) => {
                if let Some(key) = matches.get_value(subcommand_name, "key") {
                    match matches.get_value(subcommand_name, "path") {
                        Some(given_path) => {
                            add::add_favorite(db, key, WhereToAdd::Path(Path::new(&given_path)))
                        }
                        None => add::add_favorite(db, key, WhereToAdd::CurrentDirectory),
                    }
                }
            }

            Some(Command::Delete) => {
                if let Some(key) = matches.get_value(subcommand_name, "key") {
                    delete::delete_from_db(db, key);
                }
            }

            Some(Command::Rename) => {
                if let Some(old_key) = matches.get_value(subcommand_name, "old_key") {
                    if let Some(new_key) = matches.get_value(subcommand_name, "new_key") {
                        rename::rename_favorite(db, old_key, new_key);
                    }
                }
            }

            Some(Command::List) => list::list_favorites(db),

            Some(Command::Jump) => {
                if let Some(subcommand_matches) = matches.subcommand_matches(subcommand_name) {
                    if subcommand_matches.is_present("project-root") {
                        jump::jump_to(db, JumpTo::ProjectRoot);
                    } else if subcommand_matches.is_present("key"){
                        if let Some(key) = matches.get_value(subcommand_name, "key") {
                            jump::jump_to(db, JumpTo::Key(key));
                        }
                    } else if subcommand_matches.is_present("fuzzy-finder"){
                        jump::jump_to(db, JumpTo::FuzzyFinder);
                    }
                }
            }

            Some(Command::Clear) => clear::clear_db(db),

            Some(Command::Ls) => {
                if let Some(key) = matches.get_value(subcommand_name, "key") {
                    ls::ls_at_favorite(db, key);
                }
            }

            None => (),
        },

        None => {
            println!("diar: try 'diar --help' for more information");
        }
    }
}

trait GetFromArg {
    fn get_value(&self, subcommand_name: &str, value_name: &str) -> Option<String>;
}

impl GetFromArg for ArgMatches<'_> {
    fn get_value(&self, subcommand_name: &str, value_name: &str) -> Option<String> {
        self.subcommand_matches(subcommand_name.to_string())
            .and_then(|args| args.value_of(value_name.to_string()))
            .map(|name| name.to_string())
    }
}

fn rename_diar_directory() {
    let users_db_path = generate_path_string("/.diar".to_owned());
    let changed_db_path = generate_path_string("/.dir".to_owned());
    if !Path::new(&users_db_path).exists() && Path::new(&changed_db_path).exists() {
        println!("This tool requires a .diar directory.");
        println!("Since it was a .dir directory in the previous version,");
        println!("I am trying to replace your .dir directory with a .diar directory.");
        println!("Please type y or n");

        loop {
            let user_ans: String = read();
            match &*user_ans {
                "y" => {
                    let _ = fs::rename(Path::new(&changed_db_path), Path::new(&users_db_path));
                    println!("{} .dir -> .diar", "rename:".bold().bright_green());
                    break;
                }
                "n" => {
                    println!("{} new .diar directory", "generate:".bold().bright_green());
                    break;
                }
                _ => {
                    println!("Please type y or n");
                }
            }
        }
        println!();
    }
}

fn read<T: std::str::FromStr>() -> T {
    let mut s = String::new();
    std::io::stdin().read_line(&mut s).ok();
    s.trim().parse().ok().unwrap()
}