notes 0.2.108

A simple tool for taking notes. Work in progress. See: https://gitlab.com/remipassmoilesel/notes
Documentation
extern crate clap;

use clap::{App, Arg};

use crate::command_handler::Command;
use crate::default_error::DefaultError;
use crate::{PKG_AUTHORS, PKG_DESCRIPTION, PKG_NAME, PKG_VERSION};

use self::clap::ArgMatches;

pub struct CommandParser;

impl CommandParser {
    pub fn new() -> Self {
        CommandParser
    }

    pub fn parse_arguments(&self, args: Vec<String>) -> Result<Command, DefaultError> {
        let matches = App::new(PKG_NAME)
            .version(PKG_VERSION)
            .author(PKG_AUTHORS)
            .about(PKG_DESCRIPTION)
            .subcommand(
                App::new("new")
                    .alias("n")
                    .about("Create a new note")
                    .arg(Arg::with_name("title").help("The note title in one word")),
            )
            .subcommand(App::new("list").alias("l").about("List all notes from repository"))
            .subcommand(
                App::new("search")
                    .alias("s")
                    .about("Search in all repository")
                    .arg(Arg::with_name("needle").help("The pattern to search. You can use regular expressions")),
            )
            .subcommand(
                App::new("edit")
                    .alias("e")
                    .about("Edit a note with the default editor")
                    .arg(Arg::with_name("id").help("The id of the note to edit")),
            )
            .subcommand(
                App::new("delete")
                    .alias("d")
                    .about("Delete a note from repository")
                    .arg(Arg::with_name("id").help("The id of the note to delete")),
            )
            .subcommand(App::new("pull").alias("ll").about("Pull note repository"))
            .subcommand(App::new("push").alias("p").about("Push note repository"))
            .subcommand(App::new("help").alias("h").about("Show help"))
            .get_matches_from(args);
        self.build_command(matches)
    }

    fn build_command(&self, matches: ArgMatches) -> Result<Command, DefaultError> {
        if let Some(cmd_matches) = matches.subcommand_matches("new") {
            match cmd_matches.value_of("title") {
                Some(title) => return Ok(Command::New { path: title.to_string() }),
                None => return Err(DefaultError::new("You must specify a title".to_string())),
            }
        }
        if matches.subcommand_matches("list").is_some() {
            return Ok(Command::List);
        }
        if let Some(cmd_matches) = matches.subcommand_matches("search") {
            match cmd_matches.value_of("needle") {
                Some(needle) => return Ok(Command::Search { needle: needle.to_string() }),
                None => return Err(DefaultError::new("You must specify something to search".to_string())),
            }
        }
        if let Some(cmd_matches) = matches.subcommand_matches("edit") {
            match cmd_matches.value_of("id") {
                Some(id) => {
                    let numeric_id = id.parse::<usize>()?;
                    return Ok(Command::Edit { id: numeric_id });
                }
                None => return Err(DefaultError::new("You must specify a note id".to_string())),
            }
        }
        if let Some(cmd_matches) = matches.subcommand_matches("delete") {
            match cmd_matches.value_of("id") {
                Some(id) => {
                    let numeric_id = id.parse::<usize>()?;
                    return Ok(Command::Delete { id: numeric_id });
                }
                None => return Err(DefaultError::new("You must specify a note id".to_string())),
            }
        }
        if matches.subcommand_matches("pull").is_some() {
            return Ok(Command::Pull);
        }
        if matches.subcommand_matches("push").is_some() {
            return Ok(Command::Push);
        }
        if matches.subcommand_matches("help").is_some() {
            return Ok(Command::Help);
        }

        Err(DefaultError::new("Bad command, try: $ notes help".to_string()))
    }
}

impl Default for CommandParser {
    fn default() -> Self {
        CommandParser::new()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn should_match_new() {
        let args: Vec<String> = vec!["notes".to_string(), "new".to_string(), "one-word-title".to_string()];
        let cp = CommandParser::new();
        let command = cp.parse_arguments(args).unwrap();
        assert_eq!(
            command,
            Command::New {
                path: "one-word-title".to_string()
            }
        );

        let args: Vec<String> = vec!["notes".to_string(), "n".to_string(), "one-word-title".to_string()];
        let command = cp.parse_arguments(args).unwrap();
        assert_eq!(
            command,
            Command::New {
                path: "one-word-title".to_string()
            }
        );
    }

    #[test]
    fn should_match_list() {
        let cp = CommandParser::new();
        let args: Vec<String> = vec!["notes".to_string(), "list".to_string()];
        let command = cp.parse_arguments(args).unwrap();
        assert_eq!(command, Command::List);

        let args: Vec<String> = vec!["notes".to_string(), "l".to_string()];
        let command = cp.parse_arguments(args).unwrap();
        assert_eq!(command, Command::List);
    }

    #[test]
    fn should_match_search() {
        let cp = CommandParser::new();
        let args: Vec<String> = vec!["notes".to_string(), "search".to_string(), "needle".to_string()];
        let command = cp.parse_arguments(args).unwrap();
        assert_eq!(command, Command::Search { needle: "needle".to_string() });

        let args: Vec<String> = vec!["notes".to_string(), "s".to_string(), "needle".to_string()];
        let command = cp.parse_arguments(args).unwrap();
        assert_eq!(command, Command::Search { needle: "needle".to_string() });
    }

    #[test]
    fn should_match_edit() {
        let cp = CommandParser::new();
        let args: Vec<String> = vec!["notes".to_string(), "edit".to_string(), "111".to_string()];
        let command = cp.parse_arguments(args).unwrap();
        assert_eq!(command, Command::Edit { id: 111 });

        let args: Vec<String> = vec!["notes".to_string(), "e".to_string(), "111".to_string()];
        let command = cp.parse_arguments(args).unwrap();
        assert_eq!(command, Command::Edit { id: 111 });
    }

    #[test]
    fn should_match_delete() {
        let cp = CommandParser::new();
        let args: Vec<String> = vec!["notes".to_string(), "delete".to_string(), "111".to_string()];
        let command = cp.parse_arguments(args).unwrap();
        assert_eq!(command, Command::Delete { id: 111 });

        let args: Vec<String> = vec!["notes".to_string(), "d".to_string(), "111".to_string()];
        let command = cp.parse_arguments(args).unwrap();
        assert_eq!(command, Command::Delete { id: 111 });
    }

    #[test]
    fn should_match_pull() {
        let cp = CommandParser::new();
        let args: Vec<String> = vec!["notes".to_string(), "pull".to_string()];
        let command = cp.parse_arguments(args).unwrap();
        assert_eq!(command, Command::Pull);

        let args: Vec<String> = vec!["notes".to_string(), "ll".to_string()];
        let command = cp.parse_arguments(args).unwrap();
        assert_eq!(command, Command::Pull);
    }

    #[test]
    fn should_match_push() {
        let cp = CommandParser::new();
        let args: Vec<String> = vec!["notes".to_string(), "push".to_string()];
        let command = cp.parse_arguments(args).unwrap();
        assert_eq!(command, Command::Push);

        let args: Vec<String> = vec!["notes".to_string(), "p".to_string()];
        let command = cp.parse_arguments(args).unwrap();
        assert_eq!(command, Command::Push);
    }

    #[test]
    fn should_match_help() {
        let cp = CommandParser::new();
        let args: Vec<String> = vec!["notes".to_string(), "help".to_string()];
        let command = cp.parse_arguments(args).unwrap();
        assert_eq!(command, Command::Help);

        let args: Vec<String> = vec!["notes".to_string(), "h".to_string()];
        let command = cp.parse_arguments(args).unwrap();
        assert_eq!(command, Command::Help);
    }
}