aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-07-15 03:52:25 +0800
committer魏曹先生 <1992414357@qq.com>2026-07-15 03:52:25 +0800
commit559ff1a4d5d164c2fdbc29a9957e3db38651705f (patch)
tree956ce3a9974a6ebbdaa41d9332a18834b61d306c
parent963c0937abbb985021ccb6319b8734f143c7603f (diff)
refactor(pickable): redesign Pickable trait with two-phase parse API
Introduce `get_attr`, `tag`, and `pick` as the new trait methods, replacing the single `tag` method that returned a `PickerTag`. This enables the parser to first collect all argument position requirements and then resolve them in a second pass. Rename `PickerTag` to `PickerArgInfo` and reorder `PickerFlagAttr` variants to reflect parse priority (`Positional < Flag < Single < Multi`). Add `positional_or_else` helper to `PickerFlagAttr`.
-rw-r--r--mingling_picker/src/flag.rs54
-rw-r--r--mingling_picker/src/infos.rs24
-rw-r--r--mingling_picker/src/parselib.rs3
-rw-r--r--mingling_picker/src/pickable.rs105
-rw-r--r--mingling_picker/src/pickable/implements.rs50
5 files changed, 134 insertions, 102 deletions
diff --git a/mingling_picker/src/flag.rs b/mingling_picker/src/flag.rs
index 3fa3dc1..aebbc81 100644
--- a/mingling_picker/src/flag.rs
+++ b/mingling_picker/src/flag.rs
@@ -123,31 +123,49 @@ where
/// Describes the attribute (behavior) of a command-line parameter.
///
-/// This enum specifies how a parameter is parsed and processed:
-///
-/// - `Single`: The parameter accepts a single value. For example, `--name Alice`.
-///
-/// - `Multi`: The parameter accepts multiple values. For example, `--file a.txt --file b.txt`.
-///
-/// - `Flag`: The parameter is a boolean flag with no associated value.
-/// For example, `--verbose` sets a flag to `true`.
-///
-/// - `Positional`: The parameter is matched by its position in the command line,
-/// without requiring a `--name` or `-n` flag. For example, an input filename as the first argument.
+/// The ordering reflects parse priority (higher = parsed first):
+/// `Positional < Flag < Single < Multi`
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum PickerFlagAttr {
- /// Accepts multiple values (e.g., `--file a.txt --file b.txt`).
- Multi,
+ /// Positional argument matched by its position (e.g., an input file).
+ #[default]
+ Positional,
+
+ /// Boolean flag with no associated value (e.g., `--verbose`).
+ Flag,
/// Accepts a single value (e.g., `--name Alice`).
Single,
- /// Boolean flag with no associated value (e.g., `--verbose`).
- Flag,
+ /// Accepts multiple values (e.g., `--file a.txt --file b.txt`).
+ Multi,
+}
- /// Positional argument matched by its position (e.g., an input file).
- #[default]
- Positional,
+impl PickerFlagAttr {
+ /// Determines if the given `PickerFlag` represents a positional parameter.
+ ///
+ /// If the flag is positional (determined by `flag.is_positional()`), returns
+ /// `PickerFlagAttr::Positional`. Otherwise, invokes the `other` closure to
+ /// produce and return a `PickerFlagAttr`.
+ ///
+ /// # Parameters
+ ///
+ /// - `flag`: A reference to the [`PickerFlag`] to evaluate.
+ /// - `other`: A closure that returns a [`PickerFlagAttr`] when the flag is
+ /// **not** positional.
+ pub fn positional_or_else<'a, T>(
+ flag: &PickerFlag<'a, T>,
+ other: fn() -> PickerFlagAttr,
+ ) -> PickerFlagAttr
+ where
+ T: Pickable<'a> + Default,
+ {
+ if flag.is_positional() {
+ PickerFlagAttr::Positional
+ } else {
+ other()
+ }
+ }
}
#[cfg(test)]
diff --git a/mingling_picker/src/infos.rs b/mingling_picker/src/infos.rs
index 6270483..f820c9a 100644
--- a/mingling_picker/src/infos.rs
+++ b/mingling_picker/src/infos.rs
@@ -246,21 +246,7 @@ impl<Type> PickerResult<Type> {
}
}
-/// Represents a command-line argument/flag tag for a picker.
-///
-/// This struct defines how a picker argument should be parsed from the command line,
-/// including its short name (e.g., `-n`), long name (e.g., `--name`), and aliases.
-///
-/// # Fields
-///
-/// * `short` - The short form of the tag, typically a single character (e.g., `-n`).
-/// * `long` - The long form of the tag (e.g., `--name`).
-/// * `alias` - Alternative names for the tag (e.g., `["-N", "--nickname"]`).
-/// * `positional` - Whether this tag is a positional argument (no `-` or `--` prefix).
-/// * `optional` - Whether this tag is optional or required.
-/// * `multi` - Whether this tag can accept multiple values.
-/// * `is_flag` - Whether this tag participates in parsing after a `--` separator.
-pub struct PickerTag<'a> {
+pub struct PickerArgInfo<'a> {
/// The short form of the tag, e.g. `'n'` for `-n`.
pub short: Option<char>,
/// The long form of the tag, e.g. `"name"` for `--name`.
@@ -277,7 +263,7 @@ pub struct PickerTag<'a> {
pub is_flag: bool,
}
-impl<'a, T> From<PickerFlag<'a, T>> for PickerTag<'a>
+impl<'a, T> From<PickerFlag<'a, T>> for PickerArgInfo<'a>
where
T: Pickable<'a> + Default,
{
@@ -307,7 +293,7 @@ where
}
}
-impl<'a, T: Pickable<'a> + Default> From<&'a PickerFlag<'a, T>> for PickerTag<'a> {
+impl<'a, T: Pickable<'a> + Default> From<&'a PickerFlag<'a, T>> for PickerArgInfo<'a> {
fn from(value: &'a PickerFlag<'a, T>) -> Self {
let (long, alias) = match value.full.len() {
0 => (None, None),
@@ -334,7 +320,7 @@ impl<'a, T: Pickable<'a> + Default> From<&'a PickerFlag<'a, T>> for PickerTag<'a
}
}
-impl<'a> PickerTag<'a> {
+impl<'a> PickerArgInfo<'a> {
/// Create a new `PickerTag` with default values.
pub fn new() -> Self {
Self {
@@ -433,7 +419,7 @@ impl<'a> PickerTag<'a> {
}
}
-impl<'a> Default for PickerTag<'a> {
+impl<'a> Default for PickerArgInfo<'a> {
fn default() -> Self {
Self::new()
}
diff --git a/mingling_picker/src/parselib.rs b/mingling_picker/src/parselib.rs
index 21f0ba2..132045b 100644
--- a/mingling_picker/src/parselib.rs
+++ b/mingling_picker/src/parselib.rs
@@ -1,5 +1,2 @@
-mod seek;
-// pub use seek::*;
-
mod style;
pub use style::*;
diff --git a/mingling_picker/src/pickable.rs b/mingling_picker/src/pickable.rs
index 7a89631..9d3f054 100644
--- a/mingling_picker/src/pickable.rs
+++ b/mingling_picker/src/pickable.rs
@@ -1,47 +1,88 @@
-use crate::{PickerFlag, PickerResult, PickerTag};
+use crate::{PickerArgInfo, PickerArgs, PickerFlag, PickerFlagAttr, PickerResult};
mod implements;
-/// A trait for types that can be constructed from a raw string representation.
+/// `Pickable` trait defines how to parse a type instance from command-line arguments.
///
-/// Implementing this trait allows a type to be "picked" or parsed from a string,
-/// enabling deserialization or configuration loading from textual input.
+/// This trait is the core abstraction of the `Picker` argument parsing system, dividing the
+/// parsing process into two phases:
///
-/// # Requirements
+/// 1. **Tag phase ([`Pickable::tag`])**: Determines which argument positions the `Pickable` needs to handle.
+/// 2. **Pick phase ([`Pickable::pick`])**: Converts the raw strings at the tagged positions into the actual type.
///
-/// - The implementing type must be [`Sized`] and implement [`Default`].
-/// - The [`pick`] method performs the actual parsing and may fail.
+/// Types implementing this trait must also implement [`Default`], so that a default value
+/// can be used as a fallback when parsing fails.
///
-/// # Errors
+/// # Type Parameters
///
-/// Returns a [`PickerResult`] which encapsulates either a successful parse
-/// or an error indicating why the input could not be parsed.
-///
-/// # Examples
-///
-/// ```
-/// # use mingling_picker::{Pickable, PickerResult, PickerFlag, PickerTag};
-/// #[derive(Default)]
-/// struct MyType(String);
-///
-/// impl<'a> Pickable<'a> for MyType {
-/// fn tag(flag: &'a PickerFlag<'a, Self>) -> PickerTag<'a> {
-/// let tag = PickerTag::from(flag);
-/// }
-///
-/// fn pick(raw_strs: &[&str]) -> PickerResult<Self> {
-/// PickerResult::Parsed(MyType(raw_strs.join(" ")))
-/// }
-/// }
-/// ```
+/// * `'a` - Lifetime parameter, used to associate references in [`PickerFlag`].
pub trait Pickable<'a>
where
Self: Sized + Default,
{
- /// Given a [`PickerFlag`], returns a [`PickerTag`] that tells the parser
- /// about the argument's characteristics (e.g., positional, optional, multi).
- fn tag(flag: &'a PickerFlag<'a, Self>) -> PickerTag<'a>;
+ /// Returns the parse-order attribute of this flag.
+ ///
+ /// This attribute is used to inform the parser about the parse order
+ /// between different `Pickable` types.
+ /// See [`PickerFlagAttr`] for specific ordering definitions.
+ ///
+ /// # Parameters
+ ///
+ /// * `flag` - The current flag instance, which contains a reference to `Self`.
+ ///
+ /// # Returns
+ ///
+ /// Returns a [`PickerFlagAttr`] describing the parse-order attribute of this flag.
+ fn get_attr(flag: &'a PickerFlag<'a, Self>) -> PickerFlagAttr;
+
+ /// Tag phase: Determines which argument positions the `Pickable` needs to handle.
+ ///
+ /// This function receives a [`TagPhaseContext`] containing argument context information.
+ /// During this phase, the parser invokes each `Pickable` and collects the position indices
+ /// they return, in order to determine which arguments to parse later.
+ ///
+ /// # Parameters
+ ///
+ /// * `ctx` - The tag phase context, containing argument information, all parameters of the
+ /// current Picker, and an availability mask.
+ ///
+ /// # Returns
+ ///
+ /// Returns a `Vec<usize>` representing the indices of the arguments in the argument list
+ /// that this `Pickable` needs to handle.
+ fn tag(ctx: TagPhaseContext) -> Vec<usize>;
- /// Parses a `Self` value from the given raw string input.
+ /// Pick phase: Converts the raw string arguments tagged during the `tag` phase into
+ /// the actual expected type.
+ ///
+ /// This function receives a slice of the raw strings that were tagged in the `tag` step
+ /// and converts them into an instance of `Self`.
+ ///
+ /// # Parameters
+ ///
+ /// * `raw_strs` - A slice of strings containing the raw argument values to parse.
+ ///
+ /// # Returns
+ ///
+ /// Returns [`PickerResult<Self>`], i.e., the `Self` instance on success, or an appropriate
+ /// error message on failure.
fn pick(raw_strs: &[&str]) -> PickerResult<Self>;
}
+
+/// Tag phase context, providing the necessary argument and state information for
+/// [`Pickable::tag`].
+pub struct TagPhaseContext<'a> {
+ /// Argument information describing the structure and metadata of the argument
+ /// to be parsed.
+ pub arg_info: &'a PickerArgInfo<'a>,
+
+ /// A read-only list of all arguments in the current [`Picker`].
+ pub args: &'a PickerArgs<'a>,
+
+ /// Availability mask, recording positions already occupied by other `Pickable` tags.
+ ///
+ /// `mask.len()` equals `args.len()`. Where:
+ /// - `0` means the position is currently available (unoccupied);
+ /// - `1` means the position is already occupied and cannot be used again.
+ pub mask: &'a [usize],
+}
diff --git a/mingling_picker/src/pickable/implements.rs b/mingling_picker/src/pickable/implements.rs
index 665bde5..eb46334 100644
--- a/mingling_picker/src/pickable/implements.rs
+++ b/mingling_picker/src/pickable/implements.rs
@@ -1,39 +1,29 @@
-use crate::{Pickable, PickerFlag, PickerResult, PickerTag};
+use crate::{Pickable, PickerFlagAttr};
-macro_rules! impl_pickable {
- ($($t:ty),*) => {
- $(impl<'a> Pickable<'a> for $t {
- fn tag(flag: &'a PickerFlag<'a, Self>) -> PickerTag<'a> {
- flag.into()
- }
+impl<'a> Pickable<'a> for String {
+ fn get_attr(flag: &'a crate::PickerFlag<'a, Self>) -> PickerFlagAttr {
+ PickerFlagAttr::positional_or_else(flag, || PickerFlagAttr::Single)
+ }
+
+ fn tag(_ctx: super::TagPhaseContext) -> Vec<usize> {
+ vec![]
+ }
- fn pick(raw_strs: &[&str]) -> PickerResult<Self> {
- raw_strs
- .first()
- .and_then(|s| s.parse().ok())
- .map(PickerResult::Parsed)
- .unwrap_or(PickerResult::NotFound)
- }
- })*
- };
+ fn pick(_raw_strs: &[&str]) -> crate::PickerResult<Self> {
+ todo!()
+ }
}
-impl_pickable!(
- i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, usize, f32, f64, isize
-);
+impl<'a> Pickable<'a> for Vec<String> {
+ fn get_attr(flag: &'a crate::PickerFlag<'a, Self>) -> PickerFlagAttr {
+ PickerFlagAttr::positional_or_else(flag, || PickerFlagAttr::Multi)
+ }
-impl<'a> Pickable<'a> for String {
- fn tag(flag: &'a PickerFlag<'a, Self>) -> PickerTag<'a> {
- let mut tag: PickerTag = flag.into();
- tag.set_optional(false);
- tag.set_multi(false);
- tag
+ fn tag(_ctx: super::TagPhaseContext) -> Vec<usize> {
+ vec![]
}
- fn pick(raw_strs: &[&str]) -> PickerResult<Self> {
- raw_strs
- .first()
- .map(|s| PickerResult::Parsed(s.to_string()))
- .unwrap_or(PickerResult::NotFound)
+ fn pick(_raw_strs: &[&str]) -> crate::PickerResult<Self> {
+ todo!()
}
}