mod command;
mod errors;
use std::env;
use std::fs;
use std::path::Path;
use std::process;
use colored::*;
use command::InvokedTo;
use def::Describer;
use errors::Handle;
const JSON_PRETTY: bool = true;
fn main() {
match command::parse(&env::args().collect::<Vec<String>>()) {
InvokedTo::ShortHelp => help(),
InvokedTo::Help => usage(),
InvokedTo::DescribePath(p) => print_description(&p),
InvokedTo::AddDescription(p, d) => add_description(&p, &d, false),
InvokedTo::AddPattern(p, d) => add_description(&p, &d, true),
InvokedTo::Unknown => eprintln!("{}: {}", "Err".red(), "invalid argument list"),
}
}
fn help() {
eprintln!(
"{}{}{}",
"Usage\n",
" def [ <path> | add <path> <description> | pattern <path> <description> ]\n",
"Try \"def help\" for more details.",
);
process::exit(1);
}
fn usage() {
eprintln!(
"{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}",
"def keeps track of file and directory descriptions for you.\n",
"\n",
"Usage\n",
"\n",
" def <path> Print description of file/dir at path.\n",
" def add <path> <description> Add a description for file/dir at path.\n",
" def pattern <path> <description> Add a pattern to describe children of dir. A\n",
" wildcard in the pattern will be replaced with\n",
" the child's name.\n",
"\n",
"Descriptions\n",
"\n",
" To describe a file or directory use add sub-command which simply maps a description\n",
" to an absolute path.\n",
"\n",
" The pattern sub-command is used to describe all children of a directory using a\n",
" common trait. When pattern is used, a description is mapped to a dir, but is used\n",
" only to describe its children. If a wildcard \"*\" exists in the pattern, it will\n",
" be replaced by the child's name.\n",
"\n",
" For example:\n",
"\n",
" $ def pattern dir \"* is a child of dir\"\n",
" $ def dir/temp\n",
" /path/to/dir/temp: temp is a child of dir\n",
"\n",
"Descriptions and patterns are kept in ~/.config/def/config.json which maps each\n",
"description to an absolute path and can be added to or adjusted manually.",
);
process::exit(1);
}
fn add_description(path: &str, description: &str, pattern: bool) {
let mut describer = if Path::new(&config_file()).exists() {
get_describer()
} else {
fs::create_dir_all(&config_dir()).extract_or_exit("failed to create config");
Describer::new()
};
if pattern {
describer.add_pattern(&absolute_path(path), description);
} else {
describer.add_description(&absolute_path(path), description);
}
fs::write(
&config_file(),
describer
.to_json(JSON_PRETTY)
.extract_or_exit("failed to create config"),
)
.extract_or_exit("failed to write config to file")
}
fn print_description(path: &str) {
let describer = get_describer();
let path = absolute_path(path);
println!(
"{}",
match describer.describe(&path) {
Some(description) => format!("{}: {}", path.green(), description),
None => format!("{}: {}", "Err".red(), "no available description"),
}
)
}
fn get_describer() -> def::Describer {
Describer::new_from_json(
&fs::read_to_string(&config_file()).extract_or_exit("failed to read config"),
)
.extract_or_exit("invalid JSON config")
}
fn config_file() -> String {
format!(
"{}/.config/def/config.json",
env::var("HOME").extract_or_exit("failed to get $HOME"),
)
}
fn config_dir() -> String {
format!(
"{}/.config/def",
env::var("HOME").extract_or_exit("failed to get $HOME"),
)
}
fn absolute_path(path: &str) -> String {
match fs::canonicalize(path)
.extract_or_exit("failed to get absolute path")
.to_str()
{
Some(p) => p.to_string(),
None => {
eprintln!("{}: {}", "Err".red(), "path contains invalid chars");
process::exit(1);
}
}
}