1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
pub mod prelude {
    pub use super::Args;
    pub use super::CliCommand;
    pub use super::CliCommands;
    pub use super::CliOption;
    pub use super::CliOptions;
}

mod names {
    pub(super) const CMD_HELP: &str = "help";
    pub(super) const CMD_STATUS: &str = "status";
    pub(super) const CMD_DUMP_CONFIG: &str = "dump-config";
    pub(super) const OPT_DOUBLE_HELP: &str = "help";
    pub(super) const OPT_DOUBLE_VERSION: &str = "version";
    pub(super) const OPT_SINGLE_HELP: char = 'h';
    pub(super) const OPT_SINGLE_VERSION: char = 'v';
}

mod commands;
mod options;

pub use commands::{CliCommand, CliCommands};
pub use options::{CliOption, CliOptions};

use crate::error::prelude::*;
use std::convert::TryFrom;
use std::env;

pub struct Args {
    pub commands: CliCommands,
    pub options:  CliOptions,
}

impl Args {
    pub fn new() -> MyResult<Self> {
        let (commands, options) = env::args().skip(1).try_fold(
            (CliCommands::default(), CliOptions::default()),
            |(mut commands, mut options), arg| {
                if let Ok(opts) = CliOptions::try_from(arg.as_str()) {
                    options.0.append(&mut opts.into());
                    Ok((commands, options))
                } else {
                    if let Ok(cmd) = CliCommand::try_from(arg.as_str()) {
                        commands.0.push(cmd);
                        Ok((commands, options))
                    } else {
                        Err(Error::InvalidArgument(arg))
                    }
                }
            },
        )?;

        Ok(Self { commands, options })
    }
}

pub fn print_help() {
    let opt_help = {
        let opt = CliOption::Help;
        format!("-{}, --{}", opt.name_single(), opt.name_double())
    };
    let opt_vers = {
        let opt = CliOption::Version;
        format!("-{}, --{}", opt.name_single(), opt.name_double())
    };
    let cmd_status = CliCommand::Status.name();
    let cmd_help = CliCommand::Help.name();
    let cmd_dump_config = CliCommand::DumpConfig.name();

    println!(
        r#"{description}

USAGE:
    {name} [OPTIONS] [COMMAND]

OPTIONS:
    {opt_help:<opt_width$} Print this help message and exit.
    {opt_vers:<opt_width$} Print version information and exit.

COMMANDS:
    {cmd_status}
        Print the current cmus playback status
        with the format configured in the config.toml file.
        This is the default command, so you may omit this argument.
    {cmd_dump_config}
        Print the default config as TOML to stdout.
        To write the default config to the proper config file, run something like:
            mkdir -p ~/.config/{name}
            {name} {cmd_dump_config} > ~/.config/{name}/config.toml
    {cmd_help}
        Print this help message and exit."#,
        description = crate::meta::DESCRIPTION,
        name =  crate::meta::NAME,
        opt_width = 16,
        opt_help = opt_help,
        opt_vers = opt_vers,
        cmd_status = cmd_status,
        cmd_help = cmd_help,
        cmd_dump_config = cmd_dump_config,
    );
}

pub fn print_version() {
    println!("{} v{}", crate::meta::NAME, crate::meta::VERSION)
}

pub fn dump_config() {
    print!(
        r#"# DEFAULT CONFIG FOR {name}
# To write this config to the proper config file, run something like:
#     mkdir -p ~/.config/{name}
#     {name} {cmd_dump_config} > ~/.config/{name}/config.toml

{config}"#,
        name = crate::meta::NAME,
        cmd_dump_config = CliCommand::DumpConfig.name(),
        config = crate::config::DEFAULT_CONFIG
    );
}