From af1e869374d7dc4284ae12fb1259f4924d1994d0 Mon Sep 17 00:00:00 2001 From: jia2024 <14157332+jia2024@user.noreply.gitee.com> Date: Fri, 28 Jun 2024 11:04:01 +0800 Subject: [PATCH 1/2] Add enum flag Set type to "enum" and add a "choices" list. If flag value is not one of the choices, this task will fail. --- README.md | 22 ++++++++ mask-parser/src/maskfile.rs | 6 +++ mask-parser/src/parser.rs | 13 +++++ mask/src/main.rs | 12 +++++ mask/tests/arguments_and_flags_test.rs | 70 ++++++++++++++++++++++++++ mask/tests/introspect_test.rs | 2 + 6 files changed, 125 insertions(+) diff --git a/README.md b/README.md index f2dd4fa..86163f9 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,28 @@ echo "Total: $(($price * $TAX))" ~~~ ``` +Set `type` to `enum`, and add a `choices` list, `mask` will validate if the flag value is one of the choices list. + +**Example:** + +`````markdown +## print (text) + +> Print text with color + +**OPTIONS** +* color + * flags: -c --color + * type: enum + * 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. diff --git a/mask-parser/src/maskfile.rs b/mask-parser/src/maskfile.rs index 5a0c541..9056cec 100644 --- a/mask-parser/src/maskfile.rs +++ b/mask-parser/src/maskfile.rs @@ -57,6 +57,8 @@ impl Command { takes_value: false, required: false, validate_as_number: false, + validate_as_enum: false, + choices: vec![], val: "".to_string(), }); } @@ -107,6 +109,8 @@ 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 validate_as_enum: bool, // Should we validete it as a enum variant name? + pub choices: Vec, // Variants names if this flag is a enum. pub required: bool, /// Used within mask. TODO: store in a different place within mask instead of here. #[serde(skip)] @@ -124,6 +128,8 @@ impl NamedFlag { takes_value: false, required: false, validate_as_number: false, + validate_as_enum: false, + choices: vec![], val: "".to_string(), } } diff --git a/mask-parser/src/parser.rs b/mask-parser/src/parser.rs index 38d687e..2695409 100644 --- a/mask-parser/src/parser.rs +++ b/mask-parser/src/parser.rs @@ -117,6 +117,11 @@ pub fn parse(maskfile_contents: String) -> Maskfile { if val == "number" { current_option_flag.validate_as_number = true; } + + if val == "enum" { + current_option_flag.validate_as_enum = true; + current_option_flag.takes_value = true; + } } // Parse out the short and long flag names "flags" => { @@ -135,6 +140,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; } @@ -299,6 +310,8 @@ mod parse { "takes_value": false, "required": false, "validate_as_number": false, + "validate_as_enum": false, + "choices": [], }); assert_eq!( diff --git a/mask/src/main.rs b/mask/src/main.rs index 39571d7..e1bae31 100644 --- a/mask/src/main.rs +++ b/mask/src/main.rs @@ -196,6 +196,18 @@ fn get_command_options(mut cmd: Command, matches: &ArgMatches) -> Command { } } + if flag.validate_as_enum { + if !flag.choices.iter().any(|choice| choice == &raw_value) { + eprintln!( + "{} flag `{}` expects one of {:?}", + "ERROR:".red(), + flag.name, + flag.choices, + ); + std::process::exit(1); + } + } + raw_value } else { // Check if the boolean flag is present and set to "true". diff --git a/mask/tests/arguments_and_flags_test.rs b/mask/tests/arguments_and_flags_test.rs index 53970f7..b7a7d5e 100644 --- a/mask/tests/arguments_and_flags_test.rs +++ b/mask/tests/arguments_and_flags_test.rs @@ -177,6 +177,76 @@ Write-Output $sum } } +mod enum_option_flag { + use super::*; + + #[test] + fn properly_validates_flag_with_type_enum() { + let (_temp, maskfile_path) = common::maskfile( + r#" +## color + +**OPTIONS** +* val + * flags: --val + * type: enum + * 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: enum + * 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::*; diff --git a/mask/tests/introspect_test.rs b/mask/tests/introspect_test.rs index 8e8c0ff..da59702 100644 --- a/mask/tests/introspect_test.rs +++ b/mask/tests/introspect_test.rs @@ -27,6 +27,8 @@ echo something "takes_value": false, "required": false, "validate_as_number": false, + "validate_as_enum": false, + "choices": [], }); let expected_json = json!({ From 1f3c4e2d9277a0ac442057a672cb513c786f0712 Mon Sep 17 00:00:00 2001 From: plus7wist Date: Tue, 2 Jul 2024 11:49:22 +0800 Subject: [PATCH 2/2] Remove the enum type Only choices list is needed if you want to check flag value. --- README.md | 4 ++-- mask-parser/src/maskfile.rs | 5 +---- mask-parser/src/parser.rs | 6 ------ mask/src/main.rs | 20 ++++++++++---------- mask/tests/arguments_and_flags_test.rs | 24 ++++++++++++------------ mask/tests/introspect_test.rs | 1 - 6 files changed, 25 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 86163f9..f5233a9 100644 --- a/README.md +++ b/README.md @@ -154,7 +154,7 @@ echo "Total: $(($price * $TAX))" ~~~ ``` -Set `type` to `enum`, and add a `choices` list, `mask` will validate if the flag value is one of the choices list. +If you add a `choices` list, `mask` will validate if the flag value is one of them. **Example:** @@ -166,7 +166,7 @@ Set `type` to `enum`, and add a `choices` list, `mask` will validate if the flag **OPTIONS** * color * flags: -c --color - * type: enum + * type: string * choices: RED, BLUE, GREEN * desc: Color of the text. diff --git a/mask-parser/src/maskfile.rs b/mask-parser/src/maskfile.rs index 9056cec..c5f0f28 100644 --- a/mask-parser/src/maskfile.rs +++ b/mask-parser/src/maskfile.rs @@ -57,7 +57,6 @@ impl Command { takes_value: false, required: false, validate_as_number: false, - validate_as_enum: false, choices: vec![], val: "".to_string(), }); @@ -109,8 +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 validate_as_enum: bool, // Should we validete it as a enum variant name? - pub choices: Vec, // Variants names if this flag is a enum. + pub choices: Vec, // Choices of flag value. pub required: bool, /// Used within mask. TODO: store in a different place within mask instead of here. #[serde(skip)] @@ -128,7 +126,6 @@ impl NamedFlag { takes_value: false, required: false, validate_as_number: false, - validate_as_enum: false, choices: vec![], val: "".to_string(), } diff --git a/mask-parser/src/parser.rs b/mask-parser/src/parser.rs index 2695409..9971f2d 100644 --- a/mask-parser/src/parser.rs +++ b/mask-parser/src/parser.rs @@ -117,11 +117,6 @@ pub fn parse(maskfile_contents: String) -> Maskfile { if val == "number" { current_option_flag.validate_as_number = true; } - - if val == "enum" { - current_option_flag.validate_as_enum = true; - current_option_flag.takes_value = true; - } } // Parse out the short and long flag names "flags" => { @@ -310,7 +305,6 @@ mod parse { "takes_value": false, "required": false, "validate_as_number": false, - "validate_as_enum": false, "choices": [], }); diff --git a/mask/src/main.rs b/mask/src/main.rs index e1bae31..cb6f6ea 100644 --- a/mask/src/main.rs +++ b/mask/src/main.rs @@ -184,25 +184,25 @@ fn get_command_options(mut cmd: Command, matches: &ArgMatches) -> Command { .unwrap() .to_string(); - if flag.validate_as_number && raw_value != "" { - // Try converting to an integer or float to validate it - if raw_value.parse::().is_err() && raw_value.parse::().is_err() { + if !flag.choices.is_empty() { + if !flag.choices.iter().any(|choice| choice == &raw_value) { eprintln!( - "{} flag `{}` expects a numerical value", + "{} flag `{}` expects one of {:?}", "ERROR:".red(), - flag.name + flag.name, + flag.choices, ); std::process::exit(1); } } - if flag.validate_as_enum { - if !flag.choices.iter().any(|choice| choice == &raw_value) { + if flag.validate_as_number && raw_value != "" { + // Try converting to an integer or float to validate it + if raw_value.parse::().is_err() && raw_value.parse::().is_err() { eprintln!( - "{} flag `{}` expects one of {:?}", + "{} flag `{}` expects a numerical value", "ERROR:".red(), - flag.name, - flag.choices, + flag.name ); std::process::exit(1); } diff --git a/mask/tests/arguments_and_flags_test.rs b/mask/tests/arguments_and_flags_test.rs index b7a7d5e..d3bbb74 100644 --- a/mask/tests/arguments_and_flags_test.rs +++ b/mask/tests/arguments_and_flags_test.rs @@ -177,11 +177,11 @@ Write-Output $sum } } -mod enum_option_flag { +mod choices { use super::*; #[test] - fn properly_validates_flag_with_type_enum() { + fn properly_validates_flag_with_choices() { let (_temp, maskfile_path) = common::maskfile( r#" ## color @@ -189,19 +189,19 @@ mod enum_option_flag { **OPTIONS** * val * flags: --val - * type: enum + * type: string * choices: RED, BLUE, GREEN -~~~bash +```bash echo "Value: $val" -~~~ +``` -~~~powershell +```powershell param ( $in = $env:val ) Write-Output "Value: $in" -~~~ +``` "#, ); @@ -221,19 +221,19 @@ Write-Output "Value: $in" **OPTIONS** * val * flags: --val - * type: enum + * type: string * choices: RED, BLUE, GREEN -~~~bash +```bash echo "Value: $val" -~~~ +``` -~~~powershell +```powershell param ( $in = $env:val ) Write-Output "Value: $in" -~~~ +``` "#, ); diff --git a/mask/tests/introspect_test.rs b/mask/tests/introspect_test.rs index da59702..7c2a88f 100644 --- a/mask/tests/introspect_test.rs +++ b/mask/tests/introspect_test.rs @@ -27,7 +27,6 @@ echo something "takes_value": false, "required": false, "validate_as_number": false, - "validate_as_enum": false, "choices": [], });