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
use std::collections::HashMap;

use crate::{derive::KeyDescs, KeyDesc, PropertyError};

/// Application info.
#[derive(Debug)]
#[cfg_attr(docsrs, doc(cfg(feature = "args")))]
pub struct AppInfo<'a> {
    /// Application name.
    pub name: &'a str,
    /// Application version.
    pub version: &'a str,
    /// Application authors.
    pub author: Option<&'a str>,
    /// Application description.
    pub about: Option<&'a str>,
}

/// Generate [`AppInfo`] from Cargo.toml.
#[macro_export]
#[cfg_attr(docsrs, doc(cfg(feature = "args")))]
macro_rules! app_info {
    () => {
        AppInfo {
            name: std::env!("CARGO_PKG_NAME"),
            version: std::env!("CARGO_PKG_VERSION"),
            author: std::option_env!("CARGO_PKG_AUTHORS"),
            about: std::option_env!("CARGO_PKG_DESCRIPTION"),
        }
    };
}

fn parse(s: String) -> Result<(String, String), PropertyError> {
    if let Some(usize) = s.find("=") {
        return Ok((s[..usize - 1].to_string(), s[usize..].to_string()));
    }
    Err(PropertyError::parse_fail("Invalid arguments"))
}

#[cfg_attr(docsrs, doc(cfg(feature = "args")))]
/// Generate source from args.
pub fn from_args(
    desc: Vec<KeyDesc>,
    info: AppInfo<'_>,
) -> Result<HashMap<String, String>, PropertyError> {
    let help = format!("KEYS:\n{}\n", &KeyDescs(desc));

    let mut app = clap::App::new(info.name)
        .version(info.version)
        .arg(
            clap::Arg::with_name("property")
                .long("property")
                .short("P")
                .value_name("KEY=VALUE")
                .multiple(true)
                .help("Set properties."),
        )
        .after_help(help.as_str());
    if let Some(v) = info.author {
        app = app.author(v);
    }
    if let Some(v) = info.about {
        app = app.about(v);
    }
    Ok(app
        .get_matches()
        .values_of_lossy("property")
        .unwrap_or(vec![])
        .into_iter()
        .map(|f| parse(f))
        .collect::<Result<Vec<(String, String)>, PropertyError>>()?
        .into_iter()
        .collect::<HashMap<String, String>>())
}