diff options
| author | 魏曹先生 <1992414357@qq.com> | 2026-07-16 18:58:31 +0800 |
|---|---|---|
| committer | 魏曹先生 <1992414357@qq.com> | 2026-07-16 19:04:10 +0800 |
| commit | 41fe0580212b3a681fd767d331fd8875ee59b019 (patch) | |
| tree | 4032d50ecbcb1d7ce566012ade310b00a3b3d921 /mingling_picker/test/src | |
| parent | 7ab6216fd2c056ef0c155f37e4c95df90b88701c (diff) | |
feat(picker): add `Flag` value type and `Pickable` implementation
Add a new `Flag` enum in `mingling_picker::value` that explicitly
distinguishes between an absent flag (`Inactive`) and a present flag
(`Active`), along with its `Pickable` implementation. Rename internal
fields from `flag_*` to `arg_*` for consistency, update the default
naming case to `Kebab`, and enable the `mingling_support` feature in
workspace settings.
Diffstat (limited to 'mingling_picker/test/src')
| -rw-r--r-- | mingling_picker/test/src/test.rs | 1 | ||||
| -rw-r--r-- | mingling_picker/test/src/test/basic_test.rs | 8 | ||||
| -rw-r--r-- | mingling_picker/test/src/test/route_test.rs | 2 | ||||
| -rw-r--r-- | mingling_picker/test/src/test/style_test.rs | 95 | ||||
| -rw-r--r-- | mingling_picker/test/src/test/value_flag_test.rs | 174 |
5 files changed, 271 insertions, 9 deletions
diff --git a/mingling_picker/test/src/test.rs b/mingling_picker/test/src/test.rs index d3e19b3..29d52ec 100644 --- a/mingling_picker/test/src/test.rs +++ b/mingling_picker/test/src/test.rs @@ -1,3 +1,4 @@ mod basic_test; mod route_test; mod style_test; +mod value_flag_test; diff --git a/mingling_picker/test/src/test/basic_test.rs b/mingling_picker/test/src/test/basic_test.rs index bb67b8f..78b154e 100644 --- a/mingling_picker/test/src/test/basic_test.rs +++ b/mingling_picker/test/src/test/basic_test.rs @@ -41,8 +41,8 @@ fn test_bool_short_flag_present() { #[test] fn test_two_bool_flags_both_present() { - // The `arg!` macro expands the identifier `flag_a` into the string "flag_a" → --flag_a - let args = vec!["--flag_a", "--flag_b"]; + // UNIX_STYLE now uses Kebab naming: `flag_a` → "flag-a" → --flag-a + let args = vec!["--flag-a", "--flag-b"]; let (a, b) = args .to_picker() .pick(&arg![flag_a: bool]) @@ -56,7 +56,7 @@ fn test_two_bool_flags_both_present() { #[test] fn test_two_bool_flags_one_present() { - let args = vec!["--flag_a"]; + let args = vec!["--flag-a"]; let (a, b) = args .to_picker() .pick(&arg![flag_a: bool]) @@ -86,7 +86,7 @@ fn test_two_bool_flags_neither_present() { #[test] fn test_short_and_long_flags() { - let args = vec!["-a", "--long_b"]; + let args = vec!["-a", "--long-b"]; let (a, b) = args .to_picker() .pick(&arg![flag_a: bool, 'a']) diff --git a/mingling_picker/test/src/test/route_test.rs b/mingling_picker/test/src/test/route_test.rs index af5f169..9261db1 100644 --- a/mingling_picker/test/src/test/route_test.rs +++ b/mingling_picker/test/src/test/route_test.rs @@ -127,7 +127,7 @@ fn test_two_flags_second_missing_triggers_route() { #[test] fn test_two_flags_both_present_route_not_triggered() { // Both flags present → route not triggered → Ok((true, true)) - let args = vec!["--flag_a", "--flag_b"]; + let args = vec!["--flag-a", "--flag-b"]; let result: Result<(bool, bool), &'static str> = args .with_route::<&'static str>() .pick(&arg![flag_a: bool]) diff --git a/mingling_picker/test/src/test/style_test.rs b/mingling_picker/test/src/test/style_test.rs index dbe34b5..efb2185 100644 --- a/mingling_picker/test/src/test/style_test.rs +++ b/mingling_picker/test/src/test/style_test.rs @@ -185,17 +185,31 @@ fn test_snake_case_should_not_match_as_long_flag() { } #[test] -fn test_snake_case_naming_for_unix_style() { - // UNIX_STYLE uses snake case: `flag_a` stays `flag_a` → `--flag_a` +fn test_kebab_case_naming_for_unix_style() { + // UNIX_STYLE now uses Kebab case: `flag_a` → `flag-a` → `--flag-a` let mut info = PickerArgInfo::new(); info.set_long("flag_a"); - let args = vec![make_masked("--flag_a", 0)]; + let args = vec![make_masked("--flag-a", 0)]; let result = FlagMatcher::on_match_one(&args, &UNIX_STYLE, &info); assert_eq!( result, Some(0), - "--flag_a should match with snake-style UNIX_STYLE" + "--flag-a should match with kebab-style UNIX_STYLE" + ); +} + +#[test] +fn test_snake_case_rejected_by_unix_style() { + // UNIX_STYLE uses Kebab: `--flag_a` (snake) should NOT match + let mut info = PickerArgInfo::new(); + info.set_long("flag_a"); + + let args = vec![make_masked("--flag_a", 0)]; + let result = FlagMatcher::on_match_one(&args, &UNIX_STYLE, &info); + assert_eq!( + result, None, + "--flag_a should NOT match under kebab-style UNIX_STYLE" ); } @@ -213,3 +227,76 @@ fn test_powershell_pascal_case_naming() { "-Verbose should match verbose via Pascal case" ); } + +#[test] +fn test_flag_naming_kebab_matches_my_name() { + // UNIX_STYLE now uses Kebab: `my_name` → `my-name` → `--my-name` + let mut info = PickerArgInfo::new(); + info.set_long("my_name"); + + let args = vec![make_masked("--my-name", 0)]; + let result = FlagMatcher::on_match_one(&args, &UNIX_STYLE, &info); + assert_eq!( + result, + Some(0), + "--my-name should match via kebab conversion" + ); +} + +#[test] +fn test_flag_naming_kebab_rejects_my_name_underscore() { + // Kebab style: `--my_name` (snake) should NOT match + let mut info = PickerArgInfo::new(); + info.set_long("my_name"); + + let args = vec![make_masked("--my_name", 0)]; + let result = FlagMatcher::on_match_one(&args, &UNIX_STYLE, &info); + assert_eq!( + result, None, + "--my_name should NOT match under kebab-style UNIX_STYLE" + ); +} + +#[test] +fn test_flag_naming_pascal_matches_my_name() { + // `my_name` under Pascal → `MyName` → `-MyName` + let mut info = PickerArgInfo::new(); + info.set_long("my_name"); + + let args = vec![make_masked("-MyName", 0)]; + let result = FlagMatcher::on_match_one(&args, &POWERSHELL_STYLE, &info); + assert_eq!( + result, + Some(0), + "-MyName should match via Pascal conversion" + ); +} + +#[test] +fn test_flag_naming_pascal_matches_lowercase() { + // PowerShell is case-insensitive: `-myname` should also match + let mut info = PickerArgInfo::new(); + info.set_long("my_name"); + + let args = vec![make_masked("-myname", 0)]; + let result = FlagMatcher::on_match_one(&args, &POWERSHELL_STYLE, &info); + assert_eq!( + result, + Some(0), + "-myname (lowercase) should match via case-insensitive Pascal" + ); +} + +#[test] +fn test_flag_naming_pascal_rejects_my_name_underscore() { + // Pascal style: `-my_name` (underscore) should NOT match + let mut info = PickerArgInfo::new(); + info.set_long("my_name"); + + let args = vec![make_masked("-my_name", 0)]; + let result = FlagMatcher::on_match_one(&args, &POWERSHELL_STYLE, &info); + assert_eq!( + result, None, + "-my_name should NOT match under Pascal-style naming" + ); +} diff --git a/mingling_picker/test/src/test/value_flag_test.rs b/mingling_picker/test/src/test/value_flag_test.rs new file mode 100644 index 0000000..d267a27 --- /dev/null +++ b/mingling_picker/test/src/test/value_flag_test.rs @@ -0,0 +1,174 @@ +use mingling_picker::value::Flag; +use mingling_picker::{IntoPicker, macros::arg}; + +// Basic Flag — present / absent + +#[test] +fn test_flag_present() { + let flag: Flag = vec!["--verbose"] + .to_picker() + .pick(&arg![verbose: Flag]) + .unwrap(); + assert_eq!(flag, Flag::Active); +} + +#[test] +fn test_flag_absent_returns_inactive() { + // Unlike bool, Flag::pick returns Parsed(Inactive) when no match is found, + // so or_default() is NOT required — unwrap() works directly. + let flag: Flag = Vec::<&str>::new() + .to_picker() + .pick(&arg![verbose: Flag]) + .unwrap(); + assert_eq!(flag, Flag::Inactive); +} + +// Short Flag + +#[test] +fn test_flag_short_present() { + let flag: Flag = vec!["-v"] + .to_picker() + .pick(&arg![verbose: Flag, 'v']) + .unwrap(); + assert_eq!(flag, Flag::Active); +} + +// Multiple Flags + +#[test] +fn test_two_flags_both_present() { + let (a, b): (Flag, Flag) = vec!["--flag-a", "--flag-b"] + .to_picker() + .pick(&arg![flag_a: Flag]) + .pick(&arg![flag_b: Flag]) + .unwrap(); + assert_eq!(a, Flag::Active); + assert_eq!(b, Flag::Active); +} + +#[test] +fn test_two_flags_one_present() { + let (a, b): (Flag, Flag) = vec!["--flag-a"] + .to_picker() + .pick(&arg![flag_a: Flag]) + .pick(&arg![flag_b: Flag]) + .unwrap(); + assert_eq!(a, Flag::Active); + assert_eq!(b, Flag::Inactive); +} + +#[test] +fn test_two_flags_neither_present() { + let (a, b): (Flag, Flag) = Vec::<&str>::new() + .to_picker() + .pick(&arg![flag_a: Flag]) + .pick(&arg![flag_b: Flag]) + .unwrap(); + assert_eq!(a, Flag::Inactive); + assert_eq!(b, Flag::Inactive); +} + +// After `--` (end-of-options) + +#[test] +fn test_flag_after_end_of_options() { + let flag: Flag = vec!["--", "--verbose"] + .to_picker() + .pick(&arg![verbose: Flag]) + .unwrap(); + assert_eq!(flag, Flag::Inactive); +} + +// Alias + +#[test] +fn test_flag_with_alias() { + let flag: Flag = vec!["--cfg"] + .to_picker() + .pick(&arg![config: Flag, "cfg"]) + .unwrap(); + assert_eq!(flag, Flag::Active); +} + +#[test] +fn test_flag_primary_name() { + let flag: Flag = vec!["--config"] + .to_picker() + .pick(&arg![config: Flag, "cfg"]) + .unwrap(); + assert_eq!(flag, Flag::Active); +} + +// Unrelated flag should not match + +#[test] +fn test_unrelated_flag_does_not_match() { + let flag: Flag = vec!["--other"] + .to_picker() + .pick(&arg![verbose: Flag]) + .unwrap(); + assert_eq!(flag, Flag::Inactive); +} + +// to_result / to_option + +#[test] +fn test_flag_to_result() { + let result: Result<Flag, ()> = vec!["--verbose"] + .to_picker() + .pick(&arg![verbose: Flag]) + .to_result(); + assert_eq!(result, Ok(Flag::Active)); +} + +#[test] +fn test_flag_to_option() { + let opt: Option<Flag> = vec!["--verbose"] + .to_picker() + .pick(&arg![verbose: Flag]) + .to_option(); + assert_eq!(opt, Some(Flag::Active)); +} + +// Bool conversions + +#[test] +fn test_flag_converts_to_bool() { + let flag = Flag::Active; + assert!(bool::from(flag)); + + let flag = Flag::Inactive; + assert!(!bool::from(flag)); +} + +#[test] +fn test_flag_from_bool() { + assert_eq!(Flag::from(true), Flag::Active); + assert_eq!(Flag::from(false), Flag::Inactive); +} + +#[test] +fn test_flag_deref_to_bool() { + let active = Flag::Active; + assert!(*active); + + let inactive = Flag::Inactive; + assert!(!*inactive); +} + +// Flag never triggers route (unlike bool) +// +// Flag::pick always returns Parsed, so the fallback chain +// (default → route) is never entered. + +#[test] +fn test_flag_absent_does_not_trigger_route() { + // Even without or_default / or_route, absent flag returns Inactive, not a route + let result: Result<Flag, &str> = Vec::<&str>::new() + .with_route::<&str>() + .pick(&arg![verbose: Flag]) + .or_route(|| "should_not_fire") + .to_result(); + assert_eq!(result, Ok(Flag::Inactive)); +} |
