patchy/
flags.rs

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
use std::{env, fmt::Display};

use colored::Colorize;
use once_cell::sync::Lazy;

use crate::{commands::help::format_description, types::CommandArgs};

pub struct Flag<'a> {
    pub short: &'a str,
    pub long: &'a str,
    pub description: &'a str,
}

/// Extracts value out of a `flag` which can have an assignment
///
/// # Examples
///
/// ```rust
/// use patchy::flags::Flag;
///
/// let my_flag = Flag {
///     short: "-r=",
///     long: "--remote-name=",
///     description: "some flag",
/// };
///
/// let long_version = my_flag.extract_from_arg("--remote-name=abc");
/// let short_version = my_flag.extract_from_arg("-r=abcdefg");
/// let invalid = my_flag.extract_from_arg("-m=abcdefg");
///
/// assert_eq!(long_version, Some("abc".into()));
/// assert_eq!(short_version, Some("abcdefg".into()));
/// assert_eq!(invalid, None);
/// ```
impl Flag<'_> {
    pub fn is_in_args(&self, args: &CommandArgs) -> bool {
        args.contains(self.short) || args.contains(self.long)
    }

    pub fn extract_from_arg(&self, arg: &str) -> Option<String> {
        if arg.starts_with(self.short) {
            arg.get(self.short.len()..).map(|value| value.into())
        } else if arg.starts_with(self.long) {
            arg.get(self.long.len()..).map(|value| value.into())
        } else {
            None
        }
    }
}

impl Display for Flag<'_> {
    /// Formats a flag into a colored format with a description, printable to the terminal
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "{}{}{}\n    {}",
            self.short.bright_magenta(),
            ", ".bright_black(),
            self.long.bright_magenta(),
            format_description(self.description)
        )
    }
}

/// Checks whether an input argument is a valid flag
pub fn is_valid_flag(arg: &str, available_flags: &[&Flag]) -> bool {
    // TODO: flags that don't end in "=" should be compared fully, not just the beginning
    available_flags
        .iter()
        .flat_map(|flag| [flag.short, flag.long])
        .any(|flag| arg.starts_with(flag))
}

/// Makes the program output more detailed information
pub static IS_VERBOSE: Lazy<bool> = Lazy::new(|| {
    let args: CommandArgs = env::args().collect();
    args.contains("--verbose")
});