aboutsummaryrefslogtreecommitdiff
path: root/mingling_picker/src
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-07-16 18:58:31 +0800
committer魏曹先生 <1992414357@qq.com>2026-07-16 19:04:10 +0800
commit41fe0580212b3a681fd767d331fd8875ee59b019 (patch)
tree4032d50ecbcb1d7ce566012ade310b00a3b3d921 /mingling_picker/src
parent7ab6216fd2c056ef0c155f37e4c95df90b88701c (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/src')
-rw-r--r--mingling_picker/src/arg.rs14
-rw-r--r--mingling_picker/src/builtin.rs1
-rw-r--r--mingling_picker/src/builtin/pick_flag.rs21
-rw-r--r--mingling_picker/src/lib.rs3
-rw-r--r--mingling_picker/src/parselib.rs3
-rw-r--r--mingling_picker/src/parselib/arg_matcher.rs21
-rw-r--r--mingling_picker/src/parselib/style.rs4
-rw-r--r--mingling_picker/src/picker.rs8
-rw-r--r--mingling_picker/src/picker/parse.rs10
-rw-r--r--mingling_picker/src/picker/patterns.rs52
-rw-r--r--mingling_picker/src/value.rs134
11 files changed, 234 insertions, 37 deletions
diff --git a/mingling_picker/src/arg.rs b/mingling_picker/src/arg.rs
index 3b5f8b1..a18a616 100644
--- a/mingling_picker/src/arg.rs
+++ b/mingling_picker/src/arg.rs
@@ -39,6 +39,20 @@ where
pub internal_type: PhantomData<Type>,
}
+impl<'a, Type> From<&'a PickerArg<'a, Type>> for PickerArg<'a, Type>
+where
+ Type: Pickable<'a>,
+{
+ fn from(value: &'a PickerArg<'a, Type>) -> Self {
+ PickerArg {
+ full: value.full,
+ short: value.short,
+ positional: value.positional,
+ internal_type: PhantomData,
+ }
+ }
+}
+
impl<'a, Type> PickerArg<'a, Type>
where
Type: Pickable<'a>,
diff --git a/mingling_picker/src/builtin.rs b/mingling_picker/src/builtin.rs
index d6c4085..00fe1e5 100644
--- a/mingling_picker/src/builtin.rs
+++ b/mingling_picker/src/builtin.rs
@@ -1 +1,2 @@
mod pick_bool;
+mod pick_flag;
diff --git a/mingling_picker/src/builtin/pick_flag.rs b/mingling_picker/src/builtin/pick_flag.rs
new file mode 100644
index 0000000..b642a9a
--- /dev/null
+++ b/mingling_picker/src/builtin/pick_flag.rs
@@ -0,0 +1,21 @@
+use crate::parselib::{FlagMatcher, Matcher};
+use crate::pickable_needed::*;
+use crate::value::Flag;
+
+impl<'a> Pickable<'a> for Flag {
+ fn get_attr(_: &'a PickerArg<'a, Self>) -> PickerArgAttr {
+ PickerArgAttr::Flag
+ }
+
+ fn tag(ctx: TagPhaseContext) -> Vec<usize> {
+ FlagMatcher::match_all(ctx.into())
+ }
+
+ fn pick(raw_strs: &[&str]) -> PickerArgResult<Self> {
+ if raw_strs.is_empty() {
+ PickerArgResult::Parsed(Flag::Inactive)
+ } else {
+ PickerArgResult::Parsed(Flag::Active)
+ }
+ }
+}
diff --git a/mingling_picker/src/lib.rs b/mingling_picker/src/lib.rs
index c3c15b9..afc7aca 100644
--- a/mingling_picker/src/lib.rs
+++ b/mingling_picker/src/lib.rs
@@ -14,6 +14,8 @@ pub use infos::*;
pub mod parselib;
+pub mod value;
+
pub mod prelude {
pub use crate::IntoPicker;
}
@@ -36,5 +38,6 @@ pub mod matcher_needed {
#[cfg(feature = "mingling_support")]
mod corebind;
+#[allow(unused_imports)]
#[cfg(feature = "mingling_support")]
pub use corebind::*;
diff --git a/mingling_picker/src/parselib.rs b/mingling_picker/src/parselib.rs
index cf550b7..49e3cc2 100644
--- a/mingling_picker/src/parselib.rs
+++ b/mingling_picker/src/parselib.rs
@@ -1,6 +1,9 @@
mod flag_matcher;
pub use flag_matcher::*;
+mod arg_matcher;
+pub use arg_matcher::*;
+
mod style;
pub use style::*;
diff --git a/mingling_picker/src/parselib/arg_matcher.rs b/mingling_picker/src/parselib/arg_matcher.rs
new file mode 100644
index 0000000..e8be999
--- /dev/null
+++ b/mingling_picker/src/parselib/arg_matcher.rs
@@ -0,0 +1,21 @@
+use crate::matcher_needed::*;
+
+pub struct ArgMatcher;
+
+impl Matcher for ArgMatcher {
+ fn on_match_one(
+ _args: &[MaskedArg],
+ _style: &ParserStyle,
+ _arg_info: &PickerArgInfo,
+ ) -> Option<usize> {
+ todo!()
+ }
+
+ fn on_match_all(
+ _args: &[MaskedArg],
+ _style: &ParserStyle,
+ _arg_info: &PickerArgInfo,
+ ) -> Vec<usize> {
+ todo!()
+ }
+}
diff --git a/mingling_picker/src/parselib/style.rs b/mingling_picker/src/parselib/style.rs
index 434b545..4ea161f 100644
--- a/mingling_picker/src/parselib/style.rs
+++ b/mingling_picker/src/parselib/style.rs
@@ -1,7 +1,7 @@
use std::sync::OnceLock;
use std::sync::atomic::{AtomicBool, Ordering};
-use crate::parselib::ParserStyleNamingCase::{Pascal, Snake};
+use crate::parselib::ParserStyleNamingCase::{Kebab, Pascal};
/// Defines the style of command-line argument parsing (prefixes, separators, etc.).
#[derive(Clone, Copy, PartialEq, Eq)]
@@ -175,7 +175,7 @@ pub const UNIX_STYLE: ParserStyle = ParserStyle {
value_separator: '=',
case_sensitive: true,
allow_combine: true,
- naming_case: Snake,
+ naming_case: Kebab,
};
/// PowerShell style (e.g., `-Verbose`, `-Name:value`)
diff --git a/mingling_picker/src/picker.rs b/mingling_picker/src/picker.rs
index c6e40c8..4473d76 100644
--- a/mingling_picker/src/picker.rs
+++ b/mingling_picker/src/picker.rs
@@ -270,18 +270,18 @@ pub trait IntoPicker<'a> {
/// ```
fn to_picker(self) -> Picker<'a, ()>;
- /// Creates a `PickerPattern1` from the given flag for the `pick` method.
+ /// Creates a `PickerPattern1` from the given arg for the `pick` method.
///
/// This method converts the value into a `Picker` and starts a parameter
- /// picking chain with one flag. The result is initially `Unparsed`.
- fn pick<N>(self, flag: &'a PickerArg<'a, N>) -> PickerPattern1<'a, N, ()>
+ /// picking chain with one arg. The result is initially `Unparsed`.
+ fn pick<N>(self, arg: impl Into<&'a PickerArg<'a, N>>) -> PickerPattern1<'a, N, ()>
where
Self: Sized,
N: Pickable<'a> + Default + Sized,
{
PickerPattern1 {
args: self.to_picker().args,
- flag_1: flag,
+ arg_1: arg.into(),
result_1: PickerArgResult::Unparsed,
default_1: None,
route_1: None,
diff --git a/mingling_picker/src/picker/parse.rs b/mingling_picker/src/picker/parse.rs
index 69de583..8f7d514 100644
--- a/mingling_picker/src/picker/parse.rs
+++ b/mingling_picker/src/picker/parse.rs
@@ -67,13 +67,13 @@ internal_repeat!(1..=32 => {
// ArgInfos
let arg_infos: [PickerArgInfo; $] = [
(
- PickerArgInfo::from(self.flag_$),
+ PickerArgInfo::from(self.arg_$),
+)
];
let mut bundle: [
(
- // Flag Attr
+ // Arg Attr
PickerArgAttr,
// Tag Func
@@ -88,8 +88,8 @@ internal_repeat!(1..=32 => {
; $] = [
(
(
- // Flag Attr
- T$::get_attr(self.flag_$),
+ // Arg Attr
+ T$::get_attr(self.arg_$),
// Tag Func
Box::new(|args, mask| {
@@ -143,7 +143,7 @@ internal_repeat!(1..=32 => {
// Sort by Bundle Ord (descending)
bundle.sort_by(|a, b| b.0.cmp(&a.0));
- // Mask — size = number of args (not flags), so use args length
+ // Mask — size = number of args (not args), so use args length
let mut mask: Vec<u8> = vec![0u8; self.args.len()];
// Parsing
diff --git a/mingling_picker/src/picker/patterns.rs b/mingling_picker/src/picker/patterns.rs
index df04ab9..3c6e73f 100644
--- a/mingling_picker/src/picker/patterns.rs
+++ b/mingling_picker/src/picker/patterns.rs
@@ -10,7 +10,7 @@ internal_repeat!(1..=32 => {
pub args: PickerArgs<'a>,
pub error_route: Option<Route>,
(
- pub flag_$: &'a PickerArg<'a, T$>,
+ pub arg_$: &'a PickerArg<'a, T$>,
pub result_$: PickerArgResult<T$>,
pub default_$: Option<Box<dyn FnOnce() -> T$>>,
pub route_$: Option<Box<dyn FnOnce() -> Route>>,
@@ -23,16 +23,16 @@ internal_repeat!(1..=32 => {
impl<'a, (T$,+), Route> PickerPattern$<'a, (T$,+), Route>
where (T$: Pickable<'a>,+)
{
- /// Sets a default value provider for this flag.
+ /// Sets a default value provider for this arg.
///
- /// If the flag is not provided by the user at runtime, the given closure will be
+ /// If the arg is not provided by the user at runtime, the given closure will be
/// called to produce a default value. The closure is expected to return `T$`.
///
/// # Example
///
/// ```ignore
/// let pattern = picker
- /// .pick(&my_flag)
+ /// .pick(&my_arg)
/// .or(|| 42);
/// ```
#[allow(clippy::type_complexity)]
@@ -45,16 +45,16 @@ internal_repeat!(1..=32 => {
self
}
- /// Uses the default value for this flag's type if the flag is not provided.
+ /// Uses the default value for this arg's type if the arg is not provided.
///
- /// If the flag is not provided by the user at runtime, the default value for `T$`
+ /// If the arg is not provided by the user at runtime, the default value for `T$`
/// (as defined by the `Default` trait) will be used.
///
/// # Example
///
/// ```ignore
/// let pattern = picker
- /// .pick(&my_flag)
+ /// .pick(&my_arg)
/// .or_default();
/// ```
#[allow(clippy::type_complexity)]
@@ -66,16 +66,16 @@ internal_repeat!(1..=32 => {
self
}
- /// Sets a route for when the flag is not provided.
+ /// Sets a route for when the arg is not provided.
///
- /// If the flag is not provided by the user at runtime, the given closure will be
+ /// If the arg is not provided by the user at runtime, the given closure will be
/// called to produce a route value that will be returned early.
///
/// # Example
///
/// ```ignore
/// let pattern = picker
- /// .pick(&my_flag)
+ /// .pick(&my_arg)
/// .or_route(|| Redirect::home());
/// ```
pub fn or_route<F>(mut self, func: F) -> Self
@@ -91,7 +91,7 @@ internal_repeat!(1..=32 => {
/// Resets the route for this picker pattern, allowing a different route type.
///
/// This method converts the current `PickerPattern` into a new one with a different
- /// route type `NewRoute`. All existing flag configurations, defaults, and post-
+ /// route type `NewRoute`. All existing arg configurations, defaults, and post-
/// processing functions are preserved, but the `error_route` and individual
/// `route_$` fields are cleared (set to `None`).
///
@@ -104,7 +104,7 @@ internal_repeat!(1..=32 => {
args: self.args,
error_route: None,
(
- flag_$: self.flag_$,
+ arg_$: self.arg_$,
result_$: self.result_$,
default_$: self.default_$,
route_$: None,
@@ -113,9 +113,9 @@ internal_repeat!(1..=32 => {
}
}
- /// Attaches a post-processing function to this flag.
+ /// Attaches a post-processing function to this arg.
///
- /// After the flag's value is parsed (or defaulted), the given closure will be
+ /// After the arg's value is parsed (or defaulted), the given closure will be
/// invoked with the parsed value and its return value will be used as the final
/// result. This allows transforming or validating the parsed value.
///
@@ -123,7 +123,7 @@ internal_repeat!(1..=32 => {
///
/// ```ignore
/// let pattern = picker
- /// .pick(&my_flag)
+ /// .pick(&my_arg)
/// .post(|val| val * 2);
/// ```
#[allow(clippy::type_complexity)]
@@ -143,12 +143,12 @@ internal_repeat!(1..32 => {
where (T$: Pickable<'a>,+)
{
#[allow(clippy::type_complexity)]
- /// Adds a new flag to the picking chain, returning a new `PickerPattern` with one more type parameter.
+ /// Adds a new arg to the picking chain, returning a new `PickerPattern` with one more type parameter.
///
- /// This method extends the current picking pattern by appending an additional flag.
- /// The previous flags and their results are preserved as part of the new pattern.
- /// The new flag's result is initially `Unparsed`.
- pub fn pick<N>(self, flag: &'a PickerArg<'a, N>) -> PickerPattern$+<'a, (T$,+), N, Route>
+ /// This method extends the current picking pattern by appending an additional arg.
+ /// The previous args and their results are preserved as part of the new pattern.
+ /// The new arg's result is initially `Unparsed`.
+ pub fn pick<N>(self, arg: impl Into<&'a PickerArg<'a, N>>) -> PickerPattern$+<'a, (T$,+), N, Route>
where
N: Pickable<'a>,
{
@@ -158,7 +158,7 @@ internal_repeat!(1..32 => {
error_route: self.error_route,
// Current
- flag_$+: flag,
+ arg_$+: arg.into(),
result_$+: PickerArgResult::Unparsed,
default_$+: None,
route_$+: None,
@@ -166,7 +166,7 @@ internal_repeat!(1..32 => {
// Prev
(
- flag_$: self.flag_$,
+ arg_$: self.arg_$,
result_$: self.result_$,
default_$: self.default_$,
route_$: self.route_$,
@@ -178,18 +178,18 @@ internal_repeat!(1..32 => {
});
impl<'a, Route> Picker<'a, Route> {
- /// Creates a `PickerPattern1` from the given flag to start a picking chain.
+ /// Creates a `PickerPattern1` from the given arg to start a picking chain.
///
- /// This method initiates a parameter picking chain with one flag.
+ /// This method initiates a parameter picking chain with one arg.
/// The result is initially `Unparsed`.
- pub fn pick<N>(self, flag: &'a PickerArg<'a, N>) -> PickerPattern1<'a, N, Route>
+ pub fn pick<N>(self, arg: impl Into<&'a PickerArg<'a, N>>) -> PickerPattern1<'a, N, Route>
where
N: Pickable<'a>,
{
PickerPattern1 {
args: self.args,
error_route: None::<Route>,
- flag_1: flag,
+ arg_1: arg.into(),
result_1: PickerArgResult::Unparsed,
route_1: None,
default_1: None,
diff --git a/mingling_picker/src/value.rs b/mingling_picker/src/value.rs
new file mode 100644
index 0000000..7d52442
--- /dev/null
+++ b/mingling_picker/src/value.rs
@@ -0,0 +1,134 @@
+use std::{
+ fmt::{Debug, Display},
+ ops::Deref,
+};
+
+/// Parsed result of a boolean-style command-line flag.
+///
+/// `Flag` is a **value type** that can be declared in [`PickerArg`].
+/// When the user passes `--verbose` on the command line, the parsed result is `Flag::Active`;
+/// when the flag is absent, the result is `Flag::Inactive`.
+///
+/// # Why not just `bool`?
+///
+/// Unlike a raw `bool`, `Flag` carries **explicit semantics** about whether
+/// the flag was actually provided by the user (`Active`) or simply omitted
+/// (`Inactive`). This distinction matters when you want to distinguish
+/// "the user intentionally omitted the flag" from "the flag was processed but
+/// resolved to false" — the `Pickable` implementation for `Flag` always
+/// returns `Parsed(Flag::Inactive)` when no matching argument is found,
+/// rather than `NotFound`, making it always succeed with a meaningful default.
+///
+/// # Conversions
+///
+/// `Flag` interoperates seamlessly with `bool`: `Flag::Active` is `true`,
+/// `Flag::Inactive` is `false`. The [`Deref`] impl allows using a `Flag`
+/// directly in boolean contexts:
+///
+/// ```
+/// # use mingling_picker::value::Flag;
+/// let flag = Flag::Active;
+/// if *flag { /* runs */ }
+/// ```
+///
+/// [`PickerArg`]: crate::PickerArg
+#[derive(Default, Clone, Copy, PartialEq, Eq)]
+pub enum Flag {
+ /// The flag was **not** present on the command line.
+ ///
+ /// This is the default state, equivalent to `false`.
+ #[default]
+ Inactive,
+
+ /// The flag **was** present on the command line.
+ ///
+ /// Equivalent to `true`.
+ Active,
+}
+
+impl Debug for Flag {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ Self::Inactive => write!(f, "inactive"),
+ Self::Active => write!(f, "active"),
+ }
+ }
+}
+
+impl Display for Flag {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ Self::Inactive => write!(f, "inactive"),
+ Self::Active => write!(f, "active"),
+ }
+ }
+}
+
+impl Flag {
+ /// Converts this `Flag` into a `bool`.
+ ///
+ /// Returns `true` if the flag is [`Active`], `false` if [`Inactive`].
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use mingling_picker::value::Flag;
+ /// assert!(Flag::Active.bool());
+ /// assert!(!Flag::Inactive.bool());
+ /// ```
+ ///
+ /// [`Active`]: Flag::Active
+ /// [`Inactive`]: Flag::Inactive
+ #[must_use]
+ #[inline(always)]
+ pub fn bool(&self) -> bool {
+ *self == Flag::Active
+ }
+}
+
+impl PartialEq<bool> for Flag {
+ fn eq(&self, other: &bool) -> bool {
+ self.bool() == *other
+ }
+}
+
+/// Compares `bool` with `Flag` using `==`.
+impl PartialEq<Flag> for bool {
+ fn eq(&self, other: &Flag) -> bool {
+ *self == other.bool()
+ }
+}
+
+impl From<bool> for Flag {
+ fn from(value: bool) -> Self {
+ if value { Flag::Active } else { Flag::Inactive }
+ }
+}
+
+impl From<Flag> for bool {
+ fn from(val: Flag) -> Self {
+ val == Flag::Active
+ }
+}
+
+/// Allows `Flag` to be used in boolean contexts via `*flag`.
+///
+/// # Examples
+///
+/// ```
+/// # use mingling_picker::value::Flag;
+/// let flag = Flag::Active;
+/// if *flag {
+/// println!("flag is set");
+/// }
+/// ```
+impl Deref for Flag {
+ type Target = bool;
+
+ fn deref(&self) -> &bool {
+ match self {
+ Flag::Active => &true,
+ Flag::Inactive => &false,
+ }
+ }
+}