Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add choices list #111

Merged
merged 2 commits into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,28 @@ echo "Total: $(($price * $TAX))"
~~~
```

If you add a `choices` list, `mask` will validate if the flag value is one of them.

**Example:**

`````markdown
## print (text)

> Print text with color

**OPTIONS**
* color
* flags: -c --color
* type: string
* choices: RED, BLUE, GREEN
* desc: Color of the text.

```sh
COLOR=${color:RED} # Fallback to RED if not supplied
echo "$COLOR: $text"
```
`````

If you exclude the `type` field, `mask` will treat it as a `boolean` flag. If the flag is passed, its environment variable will be `"true"`, otherwise it will be unset/non-existent.

Important to note that `mask` auto injects a very common `boolean` flag called `verbose` into every single command even if it's not used, which saves a bit of typing for you. This means every command implicitly has a `-v` and `--verbose` flag already.
Expand Down
3 changes: 3 additions & 0 deletions mask-parser/src/maskfile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ impl Command {
takes_value: false,
required: false,
validate_as_number: false,
choices: vec![],
val: "".to_string(),
});
}
Expand Down Expand Up @@ -107,6 +108,7 @@ pub struct NamedFlag {
pub multiple: bool, // Can it have multiple values? (-vvv OR -i one -i two)
pub takes_value: bool, // Does it take a value? (-i value)
pub validate_as_number: bool, // Should we validate it as a number?
pub choices: Vec<String>, // Choices of flag value.
pub required: bool,
/// Used within mask. TODO: store in a different place within mask instead of here.
#[serde(skip)]
Expand All @@ -124,6 +126,7 @@ impl NamedFlag {
takes_value: false,
required: false,
validate_as_number: false,
choices: vec![],
val: "".to_string(),
}
}
Expand Down
7 changes: 7 additions & 0 deletions mask-parser/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,12 @@ pub fn parse(maskfile_contents: String) -> Maskfile {
}
}
}
"choices" => {
current_option_flag.choices = val
.split(',')
.map(|choice| choice.trim().to_owned())
.collect();
}
"required" => {
current_option_flag.required = true;
}
Expand Down Expand Up @@ -299,6 +305,7 @@ mod parse {
"takes_value": false,
"required": false,
"validate_as_number": false,
"choices": [],
});

assert_eq!(
Expand Down
12 changes: 12 additions & 0 deletions mask/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,18 @@ fn get_command_options(mut cmd: Command, matches: &ArgMatches) -> Command {
.unwrap()
.to_string();

if !flag.choices.is_empty() {
if !flag.choices.iter().any(|choice| choice == &raw_value) {
eprintln!(
"{} flag `{}` expects one of {:?}",
"ERROR:".red(),
flag.name,
flag.choices,
);
std::process::exit(1);
}
}

if flag.validate_as_number && raw_value != "" {
// Try converting to an integer or float to validate it
if raw_value.parse::<isize>().is_err() && raw_value.parse::<f32>().is_err() {
Expand Down
70 changes: 70 additions & 0 deletions mask/tests/arguments_and_flags_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,76 @@ Write-Output $sum
}
}

mod choices {
use super::*;

#[test]
fn properly_validates_flag_with_choices() {
let (_temp, maskfile_path) = common::maskfile(
r#"
## color

**OPTIONS**
* val
* flags: --val
* type: string
* choices: RED, BLUE, GREEN

```bash
echo "Value: $val"
```

```powershell
param (
$in = $env:val
)
Write-Output "Value: $in"
```
"#,
);

common::run_mask(&maskfile_path)
.cli("color --val RED")
.assert()
.stdout(contains("Value: RED"))
.success();
}

#[test]
fn out_of_choices() {
let (_temp, maskfile_path) = common::maskfile(
r#"
## color

**OPTIONS**
* val
* flags: --val
* type: string
* choices: RED, BLUE, GREEN

```bash
echo "Value: $val"
```

```powershell
param (
$in = $env:val
)
Write-Output "Value: $in"
```
"#,
);

common::run_mask(&maskfile_path)
.cli("color --val YELLOW")
.assert()
.stderr(contains(
"flag `val` expects one of [\"RED\", \"BLUE\", \"GREEN\"]",
))
.failure();
}
}

mod numerical_option_flag {
use super::*;

Expand Down
1 change: 1 addition & 0 deletions mask/tests/introspect_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ echo something
"takes_value": false,
"required": false,
"validate_as_number": false,
"choices": [],
});

let expected_json = json!({
Expand Down