mj 0.4.3

My Journal - personal tool to capture ideas, work with journals, notes and tasks in your favourite text $EDITOR.
Documentation
use clap::{App, AppSettings, Arg, ArgGroup, SubCommand};
use crate::core::JustDate;

// These are the env vars available when building with Cargo:
// - CARGO_MANIFEST_DIR
// - CARGO_PKG_AUTHORS
// - CARGO_PKG_DESCRIPTION
// - CARGO_PKG_HOMEPAGE
// - CARGO_PKG_NAME
// - CARGO_PKG_REPOSITORY
// - CARGO_PKG_VERSION
// - CARGO_PKG_VERSION_MAJOR
// - CARGO_PKG_VERSION_MINOR
// - CARGO_PKG_VERSION_PATCH
// - CARGO_PKG_VERSION_PRE

const PKG_NAME: &'static str = env!("CARGO_PKG_NAME");
const PKG_DESCRIPTION: &'static str = env!("CARGO_PKG_DESCRIPTION");
const PKG_VERSION: &'static str = env!("CARGO_PKG_VERSION");
const PKG_AUTHORS: &'static str = env!("CARGO_PKG_AUTHORS");

pub fn app<'a>() -> App<'a, 'a> {
  App::new(PKG_NAME)
    .version(PKG_VERSION)
    .author(PKG_AUTHORS)
    .long_about(PKG_DESCRIPTION)
    .settings(&[AppSettings::DeriveDisplayOrder])
    .arg(
      Arg::with_name("config")
        .short("c")
        .long("config")
        .value_name("FILE")
        .help("Sets a custom config file")
        .takes_value(true)
    )
    .subcommand(completion_subcommand())
    .subcommand(init_subcommand())
    .subcommand(ideas_subcommand())
    .subcommand(journals_subcommand())
    .subcommand(notes_subcommand())
    .subcommand(tasks_subcommand())
}

///////////////////////////////////////////////////////
// Subcommand: [completion]

fn completion_subcommand<'a>() -> App<'a, 'a> {
  SubCommand::with_name("completion")
    .about("Generates completion scripts for various shells")
    .settings(&[AppSettings::DeriveDisplayOrder])
    .arg(
      Arg::with_name("SHELL")
        .index(1)
        .required(true)
        .possible_values(&["bash", "fish", "zsh", "powershell", "elvish"])
    )
}

///////////////////////////////////////////////////////
// Subcommand: [init]

fn init_subcommand<'a>() -> App<'a, 'a> {
  SubCommand::with_name("init")
    .about("Initializes new journal repository")
    .settings(&[AppSettings::DeriveDisplayOrder])
    .arg(
      Arg::with_name("PATH")
        .index(1)
        .required(true)
    )
}

///////////////////////////////////////////////////////
// Subcommand: [ideas]

fn ideas_subcommand<'a>() -> App<'a, 'a> {
  SubCommand::with_name("ideas")
    .alias("idea")
    .alias("i")
    .about("Manages ideas")
    .settings(&[AppSettings::DeriveDisplayOrder])
    .subcommand(ideas_edit_subcommand())
    .subcommand(ideas_remove_subcommand())
    .subcommand(ideas_list_subcommand())
}

fn ideas_edit_subcommand<'a>() -> App<'a, 'a> {
  SubCommand::with_name("edit")
    .alias("e")
    .about("Creates a new idea record or modifies existing one")
    .arg(
      Arg::with_name("NAME")
        .index(1)
        .required(true)
        .help("Name of an idea")
    )
}

fn ideas_remove_subcommand<'a>() -> App<'a, 'a> {
  SubCommand::with_name("remove")
    .alias("r")
    .about("Removes existing idea record")
    .arg(
      Arg::with_name("NAME")
        .index(1)
        .required(true)
        .help("Name of an idea")
    )
}

fn ideas_list_subcommand<'a>() -> App<'a, 'a> {
  SubCommand::with_name("list")
    .alias("ls")
    .about("Lists existing ideas")
}

///////////////////////////////////////////////////////
// Subcommand: [journals]

fn journals_subcommand<'a>() -> App<'a, 'a> {
  SubCommand::with_name("journals")
    .alias("journal")
    .alias("j")
    .about("Manages journals")
    .settings(&[AppSettings::DeriveDisplayOrder])
    .subcommand(journals_edit_subcommand())
    .subcommand(journals_remove_subcommand())
    .subcommand(journals_list_subcommand())
}

fn journals_edit_subcommand<'a>() -> App<'a, 'a> {
  SubCommand::with_name("edit")
    .alias("e")
    .about("Creates a new journal or modifies existing one")
    .arg(
      Arg::with_name("DATE")
        .index(1)
        .required(true)
        .default_value("today")
        .validator(is_valid_date)
        .help("Date of a journal")
    )
}

fn journals_remove_subcommand<'a>() -> App<'a, 'a> {
  SubCommand::with_name("remove")
    .alias("r")
    .about("Removes existing journal")
    .arg(
      Arg::with_name("DATE")
        .index(1)
        .required(true)
        .default_value("today")
        .validator(is_valid_date)
        .help("Date of a journal")
    )
}

fn is_valid_date(input: String) -> Result<(), String> {
  match JustDate::parse(&input) {
    Some(_) => Ok(()),
    None => Err(String::from("cannot parse date, should be e.g. 2019-10-03 or 3d"))
  }
}

fn journals_list_subcommand<'a>() -> App<'a, 'a> {
  SubCommand::with_name("list")
    .alias("ls")
    .about("Lists existing journals")
}

///////////////////////////////////////////////////////
// Subcommand: [notes]

fn notes_subcommand<'a>() -> App<'a, 'a> {
  SubCommand::with_name("notes")
    .alias("note")
    .alias("n")
    .about("Manages notes")
    .settings(&[AppSettings::DeriveDisplayOrder])
    .subcommand(notes_edit_subcommand())
    .subcommand(notes_remove_subcommand())
    .subcommand(notes_list_subcommand())
}

fn notes_edit_subcommand<'a>() -> App<'a, 'a> {
  SubCommand::with_name("edit")
    .alias("e")
    .about("Creates a new note or modifies existing one")
    .arg(
      Arg::with_name("CATEGORY")
        .index(1)
        .required(true)
        .help("Category of a note")
    )
    .arg(
      Arg::with_name("NAME")
        .index(2)
        .required(true)
        .help("Name of a note")
    )
}

fn notes_remove_subcommand<'a>() -> App<'a, 'a> {
  SubCommand::with_name("remove")
    .alias("r")
    .about("Removes a note or all notes in a category")
    .arg(
      Arg::with_name("CATEGORY")
        .index(1)
        .required(true)
        .help("Category of a note")
    )
    .arg(
      Arg::with_name("NAME")
        .index(2)
        .help("Name of a note")
    )
}

fn notes_list_subcommand<'a>() -> App<'a, 'a> {
  SubCommand::with_name("list")
    .alias("ls")
    .about("Lists existing notes and categories")
    .arg(
      Arg::with_name("CATEGORIES")
        .long("categories")
    )
    .arg(
      Arg::with_name("CATEGORY")
        .index(1)
        .help("Category name")
    )
    .group(
      ArgGroup::with_name("flags")
        .args(&["CATEGORIES", "CATEGORY"]),
    )
}

///////////////////////////////////////////////////////
// Subcommand: [tasks]

fn tasks_subcommand<'a>() -> App<'a, 'a> {
  SubCommand::with_name("tasks")
    .alias("task")
    .alias("t")
    .about("Manages tasks")
    .settings(&[AppSettings::DeriveDisplayOrder])
    .subcommand(tasks_edit_subcommand())
    .subcommand(tasks_remove_subcommand())
    .subcommand(tasks_list_subcommand())
}

fn tasks_edit_subcommand<'a>() -> App<'a, 'a> {
  SubCommand::with_name("edit")
    .alias("e")
    .about("Edit tasks for a project")
    .arg(
      Arg::with_name("PROJECT")
        .index(1)
        .required(true)
        .help("Project name")
    )
}

fn tasks_remove_subcommand<'a>() -> App<'a, 'a> {
  SubCommand::with_name("remove")
    .alias("r")
    .about("Removes tasks for a category")
    .arg(
      Arg::with_name("PROJECT")
        .index(1)
        .required(true)
        .help("Project name")
    )
}

fn tasks_list_subcommand<'a>() -> App<'a, 'a> {
  SubCommand::with_name("list")
    .alias("ls")
    .about("Lists tasks for projects")
    .arg(
      Arg::with_name("PROJECTS")
        .long("projects")
    )
    .arg(
      Arg::with_name("PROJECT")
        .index(1)
        .help("Project name")
    )
    .group(
      ArgGroup::with_name("flags")
        .args(&["PROJECTS", "PROJECT"]),
    )
}