diff options
| author | 魏曹先生 <1992414357@qq.com> | 2026-07-15 19:31:23 +0800 |
|---|---|---|
| committer | 魏曹先生 <1992414357@qq.com> | 2026-07-15 19:33:45 +0800 |
| commit | 8b2bbdef88f47838ed0326f5c9cf86bac84babde (patch) | |
| tree | cab7e7d492e4778a8084919459e0e8f40cc0827f | |
| parent | 5894aa72a8e63f8b6d095678fe58e88fd64836a1 (diff) | |
feat(picker): add builtin bool pickable and parselib matcher framework
Add `Pickable` implementation for `bool` and the supporting `Matcher`
trait
with utility functions for argument matching. Expose `pickable_needed`
and
`matcher_needed` modules for downstream trait implementors.
| -rw-r--r-- | Cargo.lock | 1 | ||||
| -rw-r--r-- | mingling_picker/Cargo.toml | 1 | ||||
| -rw-r--r-- | mingling_picker/src/builtin.rs | 1 | ||||
| -rw-r--r-- | mingling_picker/src/builtin/pick_bool.rs | 15 | ||||
| -rw-r--r-- | mingling_picker/src/infos.rs | 5 | ||||
| -rw-r--r-- | mingling_picker/src/lib.rs | 13 | ||||
| -rw-r--r-- | mingling_picker/src/parselib.rs | 127 | ||||
| -rw-r--r-- | mingling_picker/src/parselib/bool_matcher.rs | 45 | ||||
| -rw-r--r-- | mingling_picker/src/parselib/style.rs | 143 | ||||
| -rw-r--r-- | mingling_picker/src/parselib/utils.rs | 235 | ||||
| -rw-r--r-- | mingling_picker/src/picker.rs | 18 |
11 files changed, 595 insertions, 9 deletions
@@ -465,6 +465,7 @@ dependencies = [ name = "mingling_picker" version = "0.3.0" dependencies = [ + "just_fmt", "mingling_core", "mingling_picker_macros", ] diff --git a/mingling_picker/Cargo.toml b/mingling_picker/Cargo.toml index 4cbab39..ddab33c 100644 --- a/mingling_picker/Cargo.toml +++ b/mingling_picker/Cargo.toml @@ -14,3 +14,4 @@ mingling_support = ["dep:mingling_core", "mingling_picker_macros/mingling_suppor [dependencies] mingling_core = { workspace = true, optional = true } mingling_picker_macros.workspace = true +just_fmt.workspace = true diff --git a/mingling_picker/src/builtin.rs b/mingling_picker/src/builtin.rs new file mode 100644 index 0000000..d6c4085 --- /dev/null +++ b/mingling_picker/src/builtin.rs @@ -0,0 +1 @@ +mod pick_bool; diff --git a/mingling_picker/src/builtin/pick_bool.rs b/mingling_picker/src/builtin/pick_bool.rs new file mode 100644 index 0000000..ad93a83 --- /dev/null +++ b/mingling_picker/src/builtin/pick_bool.rs @@ -0,0 +1,15 @@ +use crate::{parselib::Matcher, pickable_needed::*}; + +impl<'a> Pickable<'a> for bool { + fn get_attr(_: &'a PickerFlag<'a, Self>) -> PickerFlagAttr { + PickerFlagAttr::Flag + } + + fn tag(ctx: TagPhaseContext) -> Vec<usize> { + <bool as Matcher>::match_all(ctx.into()) + } + + fn pick(raw_strs: &[&str]) -> PickerArgResult<Self> { + PickerArgResult::Parsed(!raw_strs.is_empty()) + } +} diff --git a/mingling_picker/src/infos.rs b/mingling_picker/src/infos.rs index ff78b0a..c0309e0 100644 --- a/mingling_picker/src/infos.rs +++ b/mingling_picker/src/infos.rs @@ -261,6 +261,11 @@ impl<Type> PickerArgResult<Type> { } } +/// Represents metadata about a command-line argument or flag. +/// +/// This struct stores all relevant information about a tag/argument that can be used +/// for parsing command-line inputs. It includes the short form (e.g., `-n`), long form +/// (e.g., `--name`), aliases, and various flags that control parsing behavior. pub struct PickerArgInfo<'a> { /// The short form of the tag, e.g. `'n'` for `-n`. pub short: Option<char>, diff --git a/mingling_picker/src/lib.rs b/mingling_picker/src/lib.rs index bee2c50..7e48db7 100644 --- a/mingling_picker/src/lib.rs +++ b/mingling_picker/src/lib.rs @@ -1,3 +1,5 @@ +mod builtin; + mod picker; pub use picker::*; @@ -20,6 +22,17 @@ pub mod macros { pub use mingling_picker_macros::*; } +/// Provides the types necessary for implementing the `Pickable` trait +pub mod pickable_needed { + pub use crate::{Pickable, PickerArgResult, PickerFlag, PickerFlagAttr, TagPhaseContext}; +} + +/// Provides the types necessary for implementing the `Matcher` trait +pub mod matcher_needed { + pub use crate::PickerArgInfo; + pub use crate::parselib::{MaskedArg, Matcher, ParserStyle}; +} + #[cfg(feature = "mingling_support")] mod corebind; diff --git a/mingling_picker/src/parselib.rs b/mingling_picker/src/parselib.rs index 132045b..74c53d0 100644 --- a/mingling_picker/src/parselib.rs +++ b/mingling_picker/src/parselib.rs @@ -1,2 +1,129 @@ +mod bool_matcher; + mod style; pub use style::*; + +mod utils; +pub use utils::*; + +use crate::{PickerArgInfo, PickerArgs}; + +/// Represents a single argument with its original raw string and index. +/// +/// This is used during pattern matching to provide context about +/// which argument is being processed. +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct MaskedArg<'a> { + /// The raw string value of the argument. + pub raw: &'a str, + /// The original index of the argument in the full argument list. + pub raw_idx: usize, +} + +/// Trait for defining matching logic against masked arguments. +/// +/// Implementors can define custom strategies for matching one or all +/// arguments that pass through a mask filter. +pub trait Matcher { + /// Called when only one match is needed. + /// + /// Returns the index of the first matched argument, or `None` if no match. + fn on_match_one( + args: &[MaskedArg], + style: &ParserStyle, + arg_info: &PickerArgInfo, + ) -> Option<usize>; + + /// Called when all matches are needed. + /// + /// Returns a vector of indices of all matched arguments. + fn on_match_all( + args: &[MaskedArg], + style: &ParserStyle, + arg_info: &PickerArgInfo, + ) -> Vec<usize>; + + /// Convenience method that builds masked arguments from `PickerArgs` and a mask, + /// then calls `on_match_one`. + fn match_one<'a>(ctx: MatcherContext<'a>) -> Option<usize> { + let masked_args = build_masked_args(ctx.args, ctx.mask); + Self::on_match_one(masked_args.as_slice(), ctx.style, ctx.arg_info) + } + + /// Convenience method that builds masked arguments from `PickerArgs` and a mask, + /// then calls `on_match_all`. + fn match_all<'a>(ctx: MatcherContext<'a>) -> Vec<usize> { + let masked_args = build_masked_args(ctx.args, ctx.mask); + Self::on_match_all(masked_args.as_slice(), ctx.style, ctx.arg_info) + } +} + +/// Context for matcher operations +/// +/// This struct bundles together the key pieces of data needed during matching: +/// - `args`: The full set of parsed arguments. +/// - `mask`: A byte mask indicating which arguments are currently active (non-zero = active). +/// - `style`: The parsing style configuration. +pub struct MatcherContext<'a> { + /// The full set of parsed arguments. + pub args: &'a PickerArgs<'a>, + + /// A byte mask where non-zero values indicate the argument at that position is active/should be matched. + pub mask: &'a [u8], + + /// The parsing style configuration. + pub style: &'a ParserStyle<'a>, + + /// Metadata about the command-line argument/flag being processed. + /// + /// Contains information such as short form (`-n`), long form (`--name`), + /// aliases, and parsing flags (positional, optional, multi, is_flag). + /// Used by matchers to make decisions based on argument characteristics. + pub arg_info: &'a PickerArgInfo<'a>, +} + +impl<'a> From<&'a crate::TagPhaseContext<'a>> for MatcherContext<'a> { + fn from(ctx: &'a crate::TagPhaseContext<'a>) -> Self { + MatcherContext { + args: ctx.args, + mask: ctx.mask, + style: ParserStyle::global_style(), + arg_info: ctx.arg_info, + } + } +} + +impl<'a> From<crate::TagPhaseContext<'a>> for MatcherContext<'a> { + fn from(ctx: crate::TagPhaseContext<'a>) -> Self { + MatcherContext { + args: ctx.args, + mask: ctx.mask, + style: ParserStyle::global_style(), + arg_info: ctx.arg_info, + } + } +} + +#[inline(always)] +fn is_masked(mask: &[u8], idx: usize) -> bool { + idx < mask.len() && mask[idx] != 0 +} + +#[inline(always)] +fn build_masked_args<'a>(args: &'a PickerArgs, mask: &'a [u8]) -> Vec<MaskedArg<'a>> { + let mut cidx = 0; + args.iter() + .filter_map(|r| { + let idx = cidx; + cidx += 1; + if is_masked(mask, cidx) { + Some(MaskedArg { + raw: r, + raw_idx: idx, + }) + } else { + None + } + }) + .collect() +} diff --git a/mingling_picker/src/parselib/bool_matcher.rs b/mingling_picker/src/parselib/bool_matcher.rs new file mode 100644 index 0000000..cbcb111 --- /dev/null +++ b/mingling_picker/src/parselib/bool_matcher.rs @@ -0,0 +1,45 @@ +use crate::{ + matcher_needed::*, + parselib::{build_possible_flags, get_seeked_first, multi_seek_eq, seek_end_of_options}, + vec_string_slice, +}; + +impl Matcher for bool { + fn on_match_one( + args: &[MaskedArg], + style: &ParserStyle, + arg_info: &PickerArgInfo, + ) -> Option<usize> { + let possible_flags = build_possible_flags(style, arg_info); + let end_of_options = seek_end_of_options(args, style); + let result = get_seeked_first(multi_seek_eq( + args, + vec_string_slice!(possible_flags), + style.case_sensitive, + )); + + match (end_of_options, result) { + (Some(end), Some(current)) if current > end => None, + _ => result, + } + } + + fn on_match_all( + args: &[MaskedArg], + style: &ParserStyle, + arg_info: &PickerArgInfo, + ) -> Vec<usize> { + let possible_flags = build_possible_flags(style, arg_info); + let end_of_options = seek_end_of_options(args, style); + let result = multi_seek_eq( + args, + vec_string_slice!(possible_flags), + style.case_sensitive, + ); + + match end_of_options { + Some(end) => result.into_iter().filter(|&idx| idx <= end).collect(), + None => result, + } + } +} diff --git a/mingling_picker/src/parselib/style.rs b/mingling_picker/src/parselib/style.rs index 3ce12f7..a2a7393 100644 --- a/mingling_picker/src/parselib/style.rs +++ b/mingling_picker/src/parselib/style.rs @@ -1,6 +1,8 @@ use std::sync::OnceLock; use std::sync::atomic::{AtomicBool, Ordering}; +use crate::parselib::ParserStyleNamingCase::{Pascal, Snake}; + /// Defines the style of command-line argument parsing (prefixes, separators, etc.). #[derive(Clone, Copy, PartialEq, Eq)] pub struct ParserStyle<'a> { @@ -24,6 +26,144 @@ pub struct ParserStyle<'a> { /// Whether combining short flags is allowed (e.g., `-abc` for `-a -b -c`) pub allow_combine: bool, + + /// Naming case + pub naming_case: ParserStyleNamingCase, +} + +impl<'a> ParserStyle<'a> { + /// Formats a flag (short or long) into a full command-line option string. + /// + /// This method takes any type that can be converted into a `FlagStr` and produces + /// a complete option string by prepending the appropriate prefix. + /// + /// # Examples + /// + /// ```ignore + /// use mingling_picker::parselib::{ParserStyle, FlagStr, UNIX_STYLE}; + /// let style = &UNIX_STYLE; + /// + /// assert_eq!(style.flag_string('v'), "-v"); + /// assert_eq!(style.flag_string("verbose"), "--verbose"); + /// ``` + /// + /// # Parameters + /// + /// * `flag` - A value that can be converted to `FlagStr`, either a `char` for short flags + /// or a `&str` for long flags. + /// + /// # Returns + /// + /// A `String` with the prefix and the flag name combined. + #[must_use] + #[inline(always)] + pub fn flag_string<F>(&self, flag: F) -> String + where + F: Into<FlagStr<'a>>, + { + match flag.into() { + FlagStr::Short(short) => format!("{}{}", self.short_prefix, short), + FlagStr::Long(long) => format!("{}{}", self.long_prefix, long), + } + } +} + +/// Represents a flag name for command-line argument parsing. +/// +/// This enum can hold either a short flag (a single character, e.g., `'v'` for `-v`) +/// or a long flag (a string, e.g., `"verbose"` for `--verbose`). +/// +/// # Examples +/// +/// ``` +/// use mingling_picker::parselib::FlagStr; +/// +/// let short: FlagStr = 'v'.into(); +/// let long: FlagStr = "verbose".into(); +/// ``` +pub enum FlagStr<'a> { + /// A short flag represented by a single character. + Short(char), + /// A long flag represented by a string slice. + Long(&'a str), +} + +impl<'a> From<char> for FlagStr<'a> { + /// Converts a single character into a `FlagStr::Short`. + fn from(c: char) -> Self { + FlagStr::Short(c) + } +} + +impl<'a> From<&'a str> for FlagStr<'a> { + /// Converts a string slice into a `FlagStr::Long`. + fn from(s: &'a str) -> Self { + FlagStr::Long(s) + } +} + +impl<'a> From<&'a String> for FlagStr<'a> { + /// Converts a reference to a `String` into a `FlagStr::Long`. + fn from(s: &'a String) -> Self { + FlagStr::Long(s.as_str()) + } +} + +#[repr(u8)] +#[derive(Default, Clone, Copy, PartialEq, Eq)] +pub enum ParserStyleNamingCase { + /// snake_case format (e.g., `brew_coffee`) + #[default] + Snake, + /// camelCase format (e.g., `brewCoffee`) + Camel, + /// PascalCase format (e.g., `BrewCoffee`) + Pascal, + /// kebab-case format (e.g., `brew-coffee`) + Kebab, + /// dot.case format (e.g., `brew.coffee`) + Dot, + /// Title Case format (e.g., `Brew Coffee`) + Title, + /// lower case format (e.g., `brew coffee`) + Lower, + /// UPPER CASE format (e.g., `BREW COFFEE`) + Upper, +} + +impl ParserStyleNamingCase { + /// Converts the input string `s` to the naming case represented by this variant. + /// + /// This method takes any type `S` that can be converted into a `String` and + /// produced from a `String`, applies the corresponding case transformation, + /// and returns the result. + /// + /// # Examples + /// + /// ``` + /// use mingling_picker::parselib::ParserStyleNamingCase; + /// + /// let camel = ParserStyleNamingCase::Camel; + /// assert_eq!(camel.convert("brew_coffee"), "brewCoffee"); + /// + /// let kebab = ParserStyleNamingCase::Kebab; + /// assert_eq!(kebab.convert("BrewCoffee"), "brew-coffee"); + /// ``` + pub fn convert<S>(&self, s: S) -> S + where + S: Into<String> + From<String>, + { + match self { + ParserStyleNamingCase::Camel => just_fmt::camel_case!(s.into()).into(), + ParserStyleNamingCase::Pascal => just_fmt::pascal_case!(s.into()).into(), + ParserStyleNamingCase::Kebab => just_fmt::kebab_case!(s.into()).into(), + ParserStyleNamingCase::Snake => just_fmt::snake_case!(s.into()).into(), + ParserStyleNamingCase::Dot => just_fmt::dot_case!(s.into()).into(), + ParserStyleNamingCase::Title => just_fmt::title_case!(s.into()).into(), + ParserStyleNamingCase::Lower => just_fmt::lower_case!(s.into()).into(), + ParserStyleNamingCase::Upper => just_fmt::upper_case!(s.into()).into(), + } + } } /// Unix-like style (e.g., `--verbose`, `-v`, `--name=value`) @@ -35,6 +175,7 @@ pub const UNIX_STYLE: ParserStyle = ParserStyle { value_separator: '=', case_sensitive: true, allow_combine: true, + naming_case: Snake, }; /// PowerShell style (e.g., `-Verbose`, `-Name:value`) @@ -46,6 +187,7 @@ pub const POWERSHELL_STYLE: ParserStyle = ParserStyle { value_separator: ':', case_sensitive: false, allow_combine: false, + naming_case: Pascal, }; /// Windows-style command-line (e.g., `/Verbose`, `/Name:value`) @@ -57,6 +199,7 @@ pub const WINDOWS_STYLE: ParserStyle = ParserStyle { value_separator: ':', case_sensitive: false, allow_combine: false, + naming_case: Pascal, }; static GLOBAL_STYLE: OnceLock<ParserStyle<'static>> = OnceLock::new(); diff --git a/mingling_picker/src/parselib/utils.rs b/mingling_picker/src/parselib/utils.rs new file mode 100644 index 0000000..eefbe2f --- /dev/null +++ b/mingling_picker/src/parselib/utils.rs @@ -0,0 +1,235 @@ +use crate::{ + PickerArgInfo, + parselib::{MaskedArg, ParserStyle}, +}; + +#[inline(always)] +pub fn build_possible_flags(style: &ParserStyle, arg_info: &PickerArgInfo) -> Vec<String> { + let mut possible_flags = vec![]; + + if let Some(short) = arg_info.short { + possible_flags.push(style.flag_string(short)); + } + + if let Some(long) = arg_info.long { + possible_flags.push(style.flag_string(long)); + } + + if let Some(aliases) = &arg_info.alias { + for alias in aliases { + possible_flags.push(style.flag_string(*alias)); + } + } + + possible_flags +} + +/// Seeks the index of the end-of-options marker (`--`) in the argument list. +/// +/// This function searches for the standard end-of-options separator (`--`) +/// in the given argument list, respecting the parser's style settings +/// (e.g., case sensitivity). The end-of-options marker indicates that all +/// subsequent arguments should be treated as positional arguments, not flags. +#[must_use] +pub fn seek_end_of_options(args: &[MaskedArg], style: &ParserStyle) -> Option<usize> { + get_seeked_first(seek_eq(args, style.end_of_options, style.case_sensitive)) +} + +/// Seeks arguments in `args` that are exactly equal to the given `string`. +/// +/// Returns the indices of matching arguments. +#[must_use] +#[inline(always)] +pub fn seek_eq(args: &[MaskedArg], string: &str, case_sensitive: bool) -> Vec<usize> { + args.iter() + .filter(|arg| { + if case_sensitive { + arg.raw == string + } else { + arg.raw.eq_ignore_ascii_case(string) + } + }) + .map(|arg| arg.raw_idx) + .collect() +} + +/// Seeks arguments in `args` that contain the given `string` as a substring. +/// +/// Returns the indices of matching arguments. +#[must_use] +#[inline(always)] +pub fn seek_contains(args: &[MaskedArg], string: &str, case_sensitive: bool) -> Vec<usize> { + args.iter() + .filter(|arg| { + if case_sensitive { + arg.raw.contains(string) + } else { + arg.raw.to_lowercase().contains(&string.to_lowercase()) + } + }) + .map(|arg| arg.raw_idx) + .collect() +} + +/// Seeks arguments in `args` that start with the given `string`. +/// +/// Returns the indices of matching arguments. +#[must_use] +#[inline(always)] +pub fn seek_start_with(args: &[MaskedArg], string: &str, case_sensitive: bool) -> Vec<usize> { + args.iter() + .filter(|arg| { + if case_sensitive { + arg.raw.starts_with(string) + } else { + arg.raw.to_lowercase().starts_with(&string.to_lowercase()) + } + }) + .map(|arg| arg.raw_idx) + .collect() +} + +/// Seeks arguments in `args` that end with the given `string`. +/// +/// Returns the indices of matching arguments. +#[must_use] +#[inline(always)] +pub fn seek_end_with(args: &[MaskedArg], string: &str, case_sensitive: bool) -> Vec<usize> { + args.iter() + .filter(|arg| { + if case_sensitive { + arg.raw.ends_with(string) + } else { + arg.raw.to_lowercase().ends_with(&string.to_lowercase()) + } + }) + .map(|arg| arg.raw_idx) + .collect() +} + +/// Seeks arguments in `args` that are exactly equal to any of the given `strings`. +/// +/// Returns the indices of matching arguments. +#[must_use] +#[inline(always)] +pub fn multi_seek_eq(args: &[MaskedArg], strings: &[&str], case_sensitive: bool) -> Vec<usize> { + args.iter() + .filter(|arg| { + if case_sensitive { + strings.contains(&arg.raw) + } else { + strings.iter().any(|s| arg.raw.eq_ignore_ascii_case(s)) + } + }) + .map(|arg| arg.raw_idx) + .collect() +} + +/// Seeks arguments in `args` that contain any of the given `strings` as a substring. +/// +/// Returns the indices of matching arguments. +#[must_use] +#[inline(always)] +pub fn multi_seek_contains( + args: &[MaskedArg], + strings: &[&str], + case_sensitive: bool, +) -> Vec<usize> { + args.iter() + .filter(|arg| { + if case_sensitive { + strings.iter().any(|s| arg.raw.contains(s)) + } else { + let lower_raw = arg.raw.to_lowercase(); + strings + .iter() + .any(|s| lower_raw.contains(&s.to_lowercase())) + } + }) + .map(|arg| arg.raw_idx) + .collect() +} + +/// Seeks arguments in `args` that start with any of the given `strings`. +/// +/// Returns the indices of matching arguments. +#[must_use] +#[inline(always)] +pub fn multi_seek_start_with( + args: &[MaskedArg], + strings: &[&str], + case_sensitive: bool, +) -> Vec<usize> { + args.iter() + .filter(|arg| { + if case_sensitive { + strings.iter().any(|s| arg.raw.starts_with(s)) + } else { + let lower_raw = arg.raw.to_lowercase(); + strings + .iter() + .any(|s| lower_raw.starts_with(&s.to_lowercase())) + } + }) + .map(|arg| arg.raw_idx) + .collect() +} + +/// Seeks arguments in `args` that end with any of the given `strings`. +/// +/// Returns the indices of matching arguments. +#[must_use] +#[inline(always)] +pub fn multi_seek_end_with( + args: &[MaskedArg], + strings: &[&str], + case_sensitive: bool, +) -> Vec<usize> { + args.iter() + .filter(|arg| { + if case_sensitive { + strings.iter().any(|s| arg.raw.ends_with(s)) + } else { + let lower_raw = arg.raw.to_lowercase(); + strings + .iter() + .any(|s| lower_raw.ends_with(&s.to_lowercase())) + } + }) + .map(|arg| arg.raw_idx) + .collect() +} + +/// Converts a `&Vec<String>` into a `Vec<&str>` by borrowing each string's slice. +/// +/// This is useful for converting owned `String` vectors into borrowed `&str` slices +/// for functions that take `&[&str]` or similar parameters. +#[must_use] +#[inline(always)] +#[doc(hidden)] +pub fn vec_string_to_vec_str(input: &[String]) -> Vec<&str> { + input.iter().map(|s| s.as_str()).collect() +} + +/// Converts a `&Vec<String>` into a `Vec<&str>` by borrowing each string's slice. +/// +/// This is useful for converting owned `String` vectors into borrowed `&str` slices +/// for functions that take `&[&str]` or similar parameters. +#[macro_export] +macro_rules! vec_string_slice { + ($v:expr) => { + $v.iter() + .map(|s| s.as_str()) + .collect::<Vec<&str>>() + .as_slice() + }; +} + +/// Gets the first element from a vector of seek results, if any. +/// +/// Returns `Some(index)` if the vector is non-empty, otherwise `None`. +#[must_use] +#[inline(always)] +pub fn get_seeked_first(seeked: Vec<usize>) -> Option<usize> { + seeked.into_iter().next() +} diff --git a/mingling_picker/src/picker.rs b/mingling_picker/src/picker.rs index b1e8527..eea9611 100644 --- a/mingling_picker/src/picker.rs +++ b/mingling_picker/src/picker.rs @@ -59,7 +59,7 @@ impl<'a> PickerArgs<'a> { self.len() == 0 } - /// Returns an iterator over the arguments, yielding owned `String` values. + /// Returns an iterator over the arguments, yielding `&str` values. pub fn iter(&'a self) -> PickerIter<'a> { match self { PickerArgs::Slice(items) => PickerIter::Slice(items.iter()), @@ -91,7 +91,7 @@ impl<'a> Index<usize> for PickerArgs<'a> { } impl<'a> IntoIterator for &'a PickerArgs<'a> { - type Item = String; + type Item = &'a str; type IntoIter = PickerIter<'a>; fn into_iter(self) -> Self::IntoIter { @@ -156,7 +156,7 @@ impl<'a, Route> Picker<'a, Route> { self.args.is_empty() } - /// Returns an iterator over the arguments, yielding owned `String` values. + /// Returns an iterator over the arguments, yielding `&str` values. pub fn iter(&'a self) -> PickerIter<'a> { self.args.iter() } @@ -179,7 +179,7 @@ impl<'a, Route> Index<usize> for &Picker<'a, Route> { } impl<'a, Route> IntoIterator for &'a Picker<'a, Route> { - type Item = String; + type Item = &'a str; type IntoIter = PickerIter<'a>; fn into_iter(self) -> Self::IntoIter { @@ -187,7 +187,7 @@ impl<'a, Route> IntoIterator for &'a Picker<'a, Route> { } } -/// Iterator for `Picker` (and `PickerArgs`), yielding owned `String` values. +/// Iterator for `Picker` (and `PickerArgs`), yielding `&'a str` values. pub enum PickerIter<'a> { /// Iterates over a borrowed slice (`&[&str]`) Slice(std::slice::Iter<'a, &'a str>), @@ -198,13 +198,13 @@ pub enum PickerIter<'a> { } impl<'a> Iterator for PickerIter<'a> { - type Item = String; + type Item = &'a str; fn next(&mut self) -> Option<Self::Item> { match self { - PickerIter::Slice(iter) => iter.next().map(|s| s.to_string()), - PickerIter::Vec(iter) => iter.next().map(|s| s.to_string()), - PickerIter::Owned(iter) => iter.next().cloned(), + PickerIter::Slice(iter) => iter.next().copied(), + PickerIter::Vec(iter) => iter.next().copied(), + PickerIter::Owned(iter) => iter.next().map(|s| s.as_str()), } } |
