aboutsummaryrefslogtreecommitdiff
path: root/mingling/src
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-08-17 04:05:41 +0800
committer魏曹先生 <1992414357@qq.com>2026-08-17 04:05:41 +0800
commitaa251efb87b561f62266628f06ad341253fbbdc5 (patch)
tree7d2cc2f34f3e4a7282d8d6f0ec0f5ae4bfc87002 /mingling/src
parentc23c590330af83afb6e146bcd9b0a274b3689d22 (diff)
refactor!: remove legacy parser feature and migrate to picker
The legacy `parser` feature and its module tree (`mingling::parser`, `Argument`, `Picker`, `Pickable`, etc.) have been fully removed and replaced by the `picker` feature powered by `arg-picker`. BREAKING CHANGE: Remove `parser` feature and use `picker` instead. Migration guide: Replace `features = ["parser"]` with `features = ["picker"]` and update API usage per the provided table.
Diffstat (limited to 'mingling/src')
-rw-r--r--mingling/src/example_docs.rs367
-rw-r--r--mingling/src/features.rs11
-rw-r--r--mingling/src/lib.rs8
-rw-r--r--mingling/src/parser.rs12
-rw-r--r--mingling/src/parser/args.rs177
-rw-r--r--mingling/src/parser/picker.rs815
-rw-r--r--mingling/src/parser/picker/bools.rs143
-rw-r--r--mingling/src/parser/picker/builtin.rs113
-rw-r--r--mingling/src/parser/picker/path.rs145
-rw-r--r--mingling/src/parser/picker/path/rule.rs231
-rw-r--r--mingling/src/parser/test.rs731
11 files changed, 62 insertions, 2691 deletions
diff --git a/mingling/src/example_docs.rs b/mingling/src/example_docs.rs
index 5688793..9406ddc 100644
--- a/mingling/src/example_docs.rs
+++ b/mingling/src/example_docs.rs
@@ -1,127 +1,5 @@
// Auto generated
-/// Example Argument Parse
-///
-/// > This example demonstrates how to use the `parser` feature to parse user input
-///
-/// Run:
-/// ```bash
-/// cargo run --manifest-path examples/example-argument-parse/Cargo.toml --quiet -- transfer README.md --size 32kib
-/// cargo run --manifest-path examples/example-argument-parse/Cargo.toml --quiet -- transfer src/ --dir
-/// cargo run --manifest-path examples/example-argument-parse/Cargo.toml --quiet -- strict-transfer README.md
-/// cargo run --manifest-path examples/example-argument-parse/Cargo.toml --quiet -- strict-transfer --dir
-/// ```
-///
-/// Output:
-/// ```plaintext
-/// file: README.md (32768)
-/// dir: src/ (1048576)
-/// file: README.md (1048576)
-/// Error: name is not provided
-/// ```
-///
-/// Source code (./Cargo.toml)
-/// ```toml
-/// [package]
-/// name = "example-argument-parse"
-/// version = "0.1.0"
-/// edition = "2024"
-///
-/// [dependencies.mingling]
-/// path = "../../mingling"
-///
-/// # Enable `parser` features
-/// features = ["parser", "extras"]
-///
-/// [workspace]
-/// ```
-///
-/// Source code (./src/main.rs)
-/// ```ignore
-/// use mingling::{macros::route, prelude::*};
-/// use std::io::Write;
-///
-/// dispatcher!("transfer", EntryTransfer);
-/// dispatcher!("strict-transfer", EntryStrictTransfer);
-///
-/// pack!(ResultFile = (bool, usize, String)); // (IsDir, Size, Name)
-///
-/// #[chain]
-/// fn handle_transfer_parse(args: EntryTransfer) -> Next {
-/// // --------- IMPORTANT ---------
-/// // First parse flag arguments (like --dir/-D), then positional arguments
-/// let result: ResultFile = args
-/// // Name --dir --size 20mib
-/// // ^^^^^^^^^^^^_ first
-/// .pick::<bool>(["--dir", "-D"])
-/// // Name --dir
-/// // ^^^^^_ second (or `-D`)
-/// .pick_or::<usize>("--size", 1024 * 1024_usize)
-/// // Name
-/// // ^^^^_ finally, pick positional arg
-/// .pick::<String>(())
-/// .after(|str| str.trim().replace(' ', ""))
-/// // Unpack to tuple (is_dir, size, name)
-/// .unpack()
-/// // Convert into ResultFile
-/// .into();
-/// // --------- IMPORTANT ---------
-/// result.into()
-/// }
-///
-/// pack!(ErrorNoNameProvided = ());
-///
-/// #[chain]
-/// fn handle_strict_transfer_parse(args: EntryStrictTransfer) -> Next {
-/// // --------- IMPORTANT ---------
-/// // Strict parsing: error immediately if the name is not provided
-/// let result: ResultFile = route! { // Use `route!` to wrap a Picker that contains `or_route`
-/// args
-/// .pick::<bool>(["--dir", "-D"])
-/// .pick_or::<usize>("--size", 1024 * 1024_usize)
-/// // Finally parse the positional argument; if not found, route to `ErrorNoNameProvided`
-/// .pick_or_route::<String, _>((), ErrorNoNameProvided::default())
-/// .after(|str| str.trim().replace(' ', ""))
-/// .unpack()
-/// }
-/// // Convert into ResultFile
-/// .into();
-/// // --------- IMPORTANT ---------
-/// result.to_chain()
-/// }
-///
-/// /// Renders the parsed transfer result (file/dir, size, name).
-/// #[renderer]
-/// fn render_result_file(result: ResultFile) -> RenderResult {
-/// let (is_dir, size, name) = result.into();
-/// let mut result = RenderResult::new();
-/// writeln!(
-/// result,
-/// "{}: {} ({})",
-/// if is_dir { "dir" } else { "file" },
-/// name,
-/// size
-/// )
-/// .ok();
-/// result
-/// }
-///
-/// /// Renders the error when no name is provided.
-/// #[renderer]
-/// fn render_error_no_name_provided(_: ErrorNoNameProvided) -> RenderResult {
-/// let mut result = RenderResult::new();
-/// writeln!(result, "Error: name is not provided").ok();
-/// result
-/// }
-///
-/// gen_program!();
-///
-/// fn main() {
-/// let program = ThisProgram::new();
-/// program.exec_and_exit();
-/// }
-/// ```
-pub mod example_argument_parse {}
/// Example Argument Picker
///
/// > Demonstrates how to use Mingling's `picker` feature and `Picker` to extract typed arguments from the command line.
@@ -401,8 +279,8 @@ pub mod example_argument_picker {}
/// [dependencies.mingling]
/// path = "../../mingling"
///
-/// # Enable `parser` features
-/// features = ["async", "parser"]
+/// # Enable `picker` features
+/// features = ["async", "picker"]
///
/// # Import any async runtime, e.g. Tokio
/// [dependencies.tokio]
@@ -438,7 +316,7 @@ pub mod example_argument_picker {}
/// #[chain]
/// // vvvvv_ `async` keyword can be used directly here
/// pub async fn handle_download(args: EntryDownload) -> Next {
-/// let file_name = args.pick(()).unpack();
+/// let file_name = args.pick_or_default(&arg![String]).unwrap();
/// fake_download(file_name).await.into()
/// }
///
@@ -963,7 +841,7 @@ pub mod example_command_macro {}
/// features = [
/// # Enable `comp` features
/// "comp",
-/// "parser",
+/// "picker",
/// ]
///
/// [build-dependencies.mingling]
@@ -1013,18 +891,22 @@ pub mod example_command_macro {}
/// }
///
/// // When the user is typing `--repeat`
-/// if ctx.filling_argument(["-r", "--repeat"]) {
+/// if ctx.previous_word == "-r" || ctx.previous_word == "--repeat" {
/// return suggest! {}; // Don't suggest anything
/// }
///
/// // When the user is typing `-`
-/// if ctx.typing_argument() {
-/// return suggest! {
+/// if ctx.current_word.starts_with('-') {
+/// // Remove arguments that have already been typed by the user
+/// let typed: Vec<&str> = ctx.all_words.iter().map(String::as_str).collect();
+/// let mut set = suggest! {
/// "-r": "Number of repetitions",
/// "--repeat": "Number of repetitions",
+/// };
+/// if let Suggest::Suggest(items) = &mut set {
+/// items.retain(|item| !typed.contains(&item.suggest().as_str()));
/// }
-/// // Remove arguments that have already been typed by the user
-/// .strip_typed_argument(ctx);
+/// return set;
/// }
///
/// // Otherwise, suggest nothing
@@ -1041,9 +923,9 @@ pub mod example_command_macro {}
/// #[chain]
/// fn handle_greet(args: EntryGreet) -> Next {
/// let result: ResultName = args
-/// .pick_or(["-r", "--repeat"], 1)
-/// .pick_or((), "World")
-/// .unpack()
+/// .pick_or(&arg![repeat: u8, 'r'], || 1)
+/// .pick_or(&arg![String], || "World".to_string())
+/// .unwrap()
/// .into();
/// result.into()
/// }
@@ -1064,155 +946,6 @@ pub mod example_command_macro {}
/// gen_program!();
/// ```
pub mod example_completion {}
-/// Example Custom Pickable
-///
-/// > This example demonstrates how to use the Pickable trait to add parsing for your types
-///
-/// Run:
-/// ```bash
-/// cargo run --manifest-path examples/example-custom-pickable/Cargo.toml --quiet -- connect 127.0.0.1:5012
-/// cargo run --manifest-path examples/example-custom-pickable/Cargo.toml --quiet -- connect 127.0.0.1
-/// ```
-///
-/// Output:
-/// ```plaintext
-/// Connected to "127.0.0.1:5012"
-/// Failed to parse address
-/// ```
-///
-/// Source code (./Cargo.toml)
-/// ```toml
-/// [package]
-/// name = "example-custom-pickable"
-/// version = "0.1.0"
-/// edition = "2024"
-///
-/// [dependencies.mingling]
-/// path = "../../mingling"
-///
-/// features = ["parser", "extras"]
-///
-/// [workspace]
-/// ```
-///
-/// Source code (./src/main.rs)
-/// ```ignore
-/// use mingling::{macros::route, parser::Pickable, prelude::*, Grouped};
-/// use std::io::Write;
-///
-/// // Define types that can be recognized by Mingling
-/// // ________________________ `Pickable` trait needs to implement Default
-/// // / ________ The Grouped derive macro registers an ID for this type
-/// // | / Mingling uses this ID to identify the type
-/// // vvvvvvv vvvvvvv
-/// #[derive(Debug, Default, Clone, Grouped)]
-/// pub struct Address {
-/// pub ip: [u8; 4],
-/// pub port: u16,
-/// }
-///
-/// // --------- IMPORTANT ---------
-/// impl Pickable for Address {
-/// type Output = Address;
-/// fn pick(args: &mut mingling::parser::Argument, flag: mingling::Flag) -> Option<Self::Output> {
-/// // Extract the raw string from Argument using the Flag
-/// let raw: String = args.pick_argument(flag)?.clone();
-///
-/// // Use TryFrom to parse the address
-/// Address::try_from(raw).ok()
-/// }
-/// }
-/// // --------- IMPORTANT ---------
-///
-/// dispatcher!("connect", EntryConnect);
-/// pack!(ErrorParseAddressFailed = ());
-///
-/// #[chain]
-/// fn handle_connect(prev: EntryConnect) -> Next {
-/// let connect: Address =
-/// route! { prev.pick_or_route((), ErrorParseAddressFailed::default()).unpack() };
-/// connect.to_chain()
-/// }
-///
-/// /// Renders the connected address.
-/// #[renderer]
-/// pub fn render_address(addr: Address) -> RenderResult {
-/// let mut render_result = RenderResult::new();
-/// write!(render_result, "Connected to \"{}\"", addr).ok();
-/// render_result
-/// }
-///
-/// /// Renders the error message when address parsing fails.
-/// #[renderer]
-/// pub fn render_error_parse_address_failed(_: ErrorParseAddressFailed) -> RenderResult {
-/// let mut render_result = RenderResult::new();
-/// write!(render_result, "Failed to parse address").ok();
-/// render_result
-/// }
-///
-/// gen_program!();
-///
-/// fn main() {
-/// ThisProgram::new().exec_and_exit();
-/// }
-///
-/// // Address conversion
-///
-/// impl TryFrom<String> for Address {
-/// type Error = String;
-///
-/// fn try_from(raw: String) -> Result<Self, Self::Error> {
-/// // Expected format: "192.168.1.1:8080"
-/// let parts: Vec<&str> = raw.split(':').collect();
-/// if parts.len() != 2 {
-/// return Err("Invalid format: expected 'IP:PORT'".to_string());
-/// }
-///
-/// let ip_str = parts[0];
-/// let port_str = parts[1];
-///
-/// // Parse IP address (4 octets separated by dots)
-/// let ip_parts: Vec<&str> = ip_str.split('.').collect();
-/// if ip_parts.len() != 4 {
-/// return Err("Invalid IP address format".to_string());
-/// }
-///
-/// let mut ip = [0u8; 4];
-/// for (i, part) in ip_parts.iter().enumerate() {
-/// ip[i] = part
-/// .parse::<u8>()
-/// .map_err(|_| format!("Invalid IP octet: {part}"))?;
-/// }
-///
-/// // Parse port
-/// let port = port_str
-/// .parse::<u16>()
-/// .map_err(|_| format!("Invalid port: {port_str}"))?;
-///
-/// Ok(Address { ip, port })
-/// }
-/// }
-///
-/// impl From<Address> for String {
-/// fn from(addr: Address) -> String {
-/// format!(
-/// "{}.{}.{}.{}:{}",
-/// addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3], addr.port
-/// )
-/// }
-/// }
-///
-/// impl std::fmt::Display for Address {
-/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-/// write!(
-/// f,
-/// "{}.{}.{}.{}:{}",
-/// self.ip[0], self.ip[1], self.ip[2], self.ip[3], self.port
-/// )
-/// }
-/// }
-/// ```
-pub mod example_custom_pickable {}
/// Example Dispatch Tree
///
/// > This example will introduce how to use `dispatch_tree`
@@ -1317,7 +1050,7 @@ pub mod example_dispatch_tree {}
///
/// features = [
/// "comp",
-/// "parser"
+/// "picker"
/// ]
///
/// [workspace]
@@ -1326,8 +1059,10 @@ pub mod example_dispatch_tree {}
/// Source code (./src/main.rs)
/// ```ignore
/// use mingling::{
-/// macros::suggest_enum, parser::PickableEnum, prelude::*, EnumTag, Grouped, ShellContext,
-/// Suggest,
+/// EnumTag, Grouped, ShellContext, Suggest,
+/// macros::suggest_enum,
+/// picker::{PickerArgResult, SinglePickable},
+/// prelude::*,
/// };
/// use std::io::Write;
///
@@ -1367,9 +1102,7 @@ pub mod example_dispatch_tree {}
/// #[enum_desc("A general-purpose programming language with clean syntax, known for readability")]
/// Python,
///
-/// #[enum_desc(
-/// "An object-oriented scripting language, famous for its concise and elegant syntax"
-/// )]
+/// #[enum_desc("An object-oriented scripting language, famous for its concise and elegant syntax")]
/// Ruby,
///
/// #[default]
@@ -1378,9 +1111,31 @@ pub mod example_dispatch_tree {}
/// }
///
/// // --------- IMPORTANT ---------
-/// // Implement the PickableEnum trait for ProgrammingLanguages,
-/// // so that `Picker` can parse this enum
-/// impl PickableEnum for ProgrammingLanguages {}
+/// // NOTE: Due to the migration from the legacy `parser` to `picker`, the `EnumTag` -> `Picker` path
+/// // is not yet complete, so a manual implementation is used for now.
+/// // Once that path is complete, `#[derive(EnumTag)]` can automatically implement `SinglePickable`,
+/// // replacing this manual implementation.
+/// impl SinglePickable for ProgrammingLanguages {
+/// fn pick_single(str: Option<&str>) -> PickerArgResult<Self> {
+/// let Some(str) = str else {
+/// return PickerArgResult::NotFound;
+/// };
+/// let lang = match str.to_lowercase().as_str() {
+/// "c" => Self::C,
+/// "c++" | "cpp" => Self::CPlusPlus,
+/// "c#" | "csharp" => Self::Csharp,
+/// "java" => Self::Java,
+/// "javascript" | "js" => Self::JavaScript,
+/// "kotlin" => Self::Kotlin,
+/// "ocaml" => Self::OCaml,
+/// "python" => Self::Python,
+/// "ruby" => Self::Ruby,
+/// "rust" => Self::Rust,
+/// _ => return PickerArgResult::NotFound,
+/// };
+/// PickerArgResult::Parsed(lang)
+/// }
+/// }
/// // --------- IMPORTANT ---------
///
/// dispatcher!("lang-select", EntryLanguageSelection);
@@ -1388,7 +1143,7 @@ pub mod example_dispatch_tree {}
/// #[chain]
/// fn handle_language_selection(args: EntryLanguageSelection) -> Next {
/// // You can use Picker to directly parse ProgrammingLanguages
-/// let lang: ProgrammingLanguages = args.pick(()).unpack();
+/// let lang: ProgrammingLanguages = args.pick_or_default(&arg![ProgrammingLanguages]).unwrap();
/// lang.into()
/// }
///
@@ -2396,7 +2151,7 @@ pub mod example_pack_err {}
///
/// [dependencies.mingling]
/// path = "../../mingling"
-/// features = ["parser"]
+/// features = ["picker"]
///
/// # Enable panic unwinding in release builds
/// [profile.release]
@@ -2437,7 +2192,7 @@ pub mod example_pack_err {}
///
/// #[chain]
/// fn handle_panic(prev: EntryPanic) -> Next {
-/// let panic_info = prev.pick::<Option<String>>(()).unpack();
+/// let panic_info = prev.pick_or_default(&arg![Option<String>]).unwrap();
/// match panic_info {
/// Some(s) => {
/// // Panic happens here, will be caught
@@ -2536,7 +2291,7 @@ pub mod example_pathfinder {}
///
/// [dependencies.mingling]
/// path = "../../mingling"
-/// features = ["repl", "parser", "extras"]
+/// features = ["repl", "picker", "extras"]
///
/// [dependencies]
/// just_fmt = "0.1.2"
@@ -2626,7 +2381,7 @@ pub mod example_pathfinder {}
/// // Parse cd command arguments
/// #[chain]
/// fn parse_cd_args(prev: EntryCd) -> Next {
-/// let join = prev.pick(()).unpack();
+/// let join = prev.pick_or_default(&arg![String]).unwrap();
/// StateChangeDirectory::new(join).into()
/// }
///
@@ -2745,7 +2500,7 @@ pub mod example_repl_basic {}
///
/// [dependencies.mingling]
/// path = "../../mingling"
-/// features = ["parser"]
+/// features = ["picker"]
///
/// [workspace]
/// ```
@@ -2787,7 +2542,7 @@ pub mod example_repl_basic {}
/// fn render_modify_current(args: EntryModifyCurrent, current_dir: &mut ResCurrentDir) -> Next {
/// current_dir.current_dir = current_dir
/// .current_dir
-/// .join(args.pick::<String>(()).unpack());
+/// .join(args.pick_or_default(&arg![String]).unwrap());
/// EntryCurrent::default().into()
/// }
///
@@ -2940,7 +2695,7 @@ pub mod example_setup {}
/// features = [
/// "structural_renderer",
/// "yaml_serde_fmt",
-/// "parser",
+/// "picker",
/// ]
///
/// [workspace]
@@ -2948,8 +2703,8 @@ pub mod example_setup {}
///
/// Source code (./src/main.rs)
/// ```ignore
-/// use mingling::prelude::*;
-/// use mingling::{parser::Picker, setup::StructuralRendererSetup, Grouped, StructuralData};
+/// use mingling::setup::picker::StructuralRendererSetup;
+/// use mingling::{Grouped, StructuralData, prelude::*};
/// use serde::Serialize;
/// use std::io::Write;
///
@@ -2986,10 +2741,10 @@ pub mod example_setup {}
///
/// #[chain]
/// fn parse_render(prev: EntryRender) -> Next {
-/// let (name, age) = Picker::new(prev.inner)
-/// .pick::<String>(())
-/// .pick::<i32>(())
-/// .unpack();
+/// let (name, age) = prev
+/// .pick_or_default(&arg![String])
+/// .pick_or_default(&arg![i32])
+/// .unwrap();
/// Info { name, age }.to_render()
/// }
///
diff --git a/mingling/src/features.rs b/mingling/src/features.rs
index 2925f03..9445328 100644
--- a/mingling/src/features.rs
+++ b/mingling/src/features.rs
@@ -229,17 +229,6 @@ pub const MINGLING_NIGHTLY: bool = false;
#[cfg(feature = "nightly")]
#[allow(unused)]
pub const MINGLING_NIGHTLY: bool = true;
-/// Whether the `parser` feature is enabled
-/// Current: `disabled`
-#[cfg(not(feature = "parser"))]
-#[allow(unused)]
-pub const MINGLING_PARSER: bool = false;
-
-/// Whether the `parser` feature is enabled
-/// Current: `enabled`
-#[cfg(feature = "parser")]
-#[allow(unused)]
-pub const MINGLING_PARSER: bool = true;
/// Whether the `pathf` feature is enabled
/// Current: `disabled`
#[cfg(not(feature = "pathf"))]
diff --git a/mingling/src/lib.rs b/mingling/src/lib.rs
index 3707289..2596a9c 100644
--- a/mingling/src/lib.rs
+++ b/mingling/src/lib.rs
@@ -36,10 +36,6 @@ pub use mingling::*;
#[cfg(feature = "core")]
pub use mingling_core as mingling;
-/// `Mingling` argument parser (Built-in)
-#[cfg(feature = "parser")]
-pub mod parser;
-
/// `Mingling` argument parser (Picker2)
#[cfg(feature = "picker")]
pub mod picker;
@@ -231,11 +227,9 @@ pub mod prelude {
pub use mingling_macros::r_println;
#[cfg(all(feature = "macros", feature = "comp"))]
+ #[cfg(feature = "comp")]
pub use crate::macros::completion;
- #[cfg(feature = "parser")]
- pub use crate::parser::AsPicker;
-
#[cfg(feature = "picker")]
pub use arg_picker::prelude::arg;
diff --git a/mingling/src/parser.rs b/mingling/src/parser.rs
deleted file mode 100644
index 97124ca..0000000
--- a/mingling/src/parser.rs
+++ /dev/null
@@ -1,12 +0,0 @@
-// Doc Not Optimize
-mod args;
-pub use crate::parser::args::*;
-
-mod picker;
-pub use crate::parser::picker::*;
-
-pub use crate::parser::picker::bools::*;
-pub use crate::parser::picker::path::*;
-
-#[cfg(test)]
-mod test;
diff --git a/mingling/src/parser/args.rs b/mingling/src/parser/args.rs
deleted file mode 100644
index c7139c4..0000000
--- a/mingling/src/parser/args.rs
+++ /dev/null
@@ -1,177 +0,0 @@
-// Doc Not Optimize
-use std::mem::replace;
-
-use mingling_core::{Flag, special_argument, special_arguments, special_flag};
-
-/// User input arguments
-#[derive(Debug, Default, Clone)]
-pub struct Argument {
- vec: Vec<String>,
-}
-
-impl From<Vec<&str>> for Argument {
- fn from(vec: Vec<&str>) -> Self {
- Self {
- vec: vec
- .into_iter()
- .map(std::string::ToString::to_string)
- .collect(),
- }
- }
-}
-
-impl From<&'static str> for Argument {
- fn from(s: &'static str) -> Self {
- Self {
- vec: vec![s.to_string()],
- }
- }
-}
-
-impl From<&'static [&'static str]> for Argument {
- fn from(slice: &'static [&'static str]) -> Self {
- Self {
- vec: slice.iter().map(|&s| s.to_string()).collect(),
- }
- }
-}
-
-impl<const N: usize> From<[&'static str; N]> for Argument {
- fn from(slice: [&'static str; N]) -> Self {
- Self {
- vec: slice.iter().map(|&s| s.to_string()).collect(),
- }
- }
-}
-
-impl<const N: usize> From<&'static [&'static str; N]> for Argument {
- fn from(slice: &'static [&'static str; N]) -> Self {
- Self {
- vec: slice.iter().map(|&s| s.to_string()).collect(),
- }
- }
-}
-
-impl From<Vec<String>> for Argument {
- fn from(vec: Vec<String>) -> Self {
- Self { vec }
- }
-}
-
-impl AsRef<[String]> for Argument {
- fn as_ref(&self) -> &[String] {
- &self.vec
- }
-}
-
-impl std::ops::Deref for Argument {
- type Target = Vec<String>;
-
- fn deref(&self) -> &Self::Target {
- &self.vec
- }
-}
-
-impl std::ops::DerefMut for Argument {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.vec
- }
-}
-
-impl Argument {
- /// Picks a single argument with the given flag
- pub fn pick_argument<F>(&mut self, flag: F) -> Option<String>
- where
- F: Into<Flag>,
- {
- if self.is_empty() {
- return None;
- }
-
- let flag: Flag = flag.into();
- if flag.is_empty() {
- // No flag
- return Some(self.vec.remove(0));
- }
- // Has any flag
- for argument in flag.iter() {
- let value = special_argument!(self.vec, argument);
- if value.is_some() {
- return value;
- }
- }
- None
- }
-
- /// Picks arguments with the given flag
- pub fn pick_arguments<F>(&mut self, flag: F) -> Vec<String>
- where
- F: Into<Flag>,
- {
- let mut str_result = Vec::new();
-
- if self.is_empty() {
- return str_result;
- }
-
- let flag: Flag = flag.into();
- if flag.is_empty() {
- let value = special_arguments!(self.vec, "");
- str_result.extend(value);
- } else {
- for argument in flag.iter() {
- let value = special_arguments!(self.vec, argument);
- str_result.extend(value);
- }
- }
-
- str_result
- }
-
- /// Picks a flag with the given flag
- pub fn pick_flag<F>(&mut self, flag: F) -> bool
- where
- F: Into<Flag>,
- {
- if self.is_empty() {
- return false;
- }
-
- let flag: Flag = flag.into();
- if flag.is_empty() {
- let first = self.vec.remove(0);
- let first_lower = first.to_lowercase();
- let trimmed = first_lower.trim();
- let result = match trimmed {
- "y" | "yes" | "true" | "1" => return true,
- "n" | "no" | "false" | "0" => return false,
- _ => false,
- };
- return result;
- }
- // Has any flag
- for argument in flag.iter() {
- let enabled = special_flag!(self.vec, argument);
- if enabled {
- return enabled;
- }
- }
- false
- }
-
- /// Dump all remaining arguments
- pub const fn dump_remains(&mut self) -> Vec<String> {
- let new = Vec::new();
- replace(&mut self.vec, new)
- }
-
- /// Removes all arguments that start with a dash ('-')
- ///
- /// This method filters out all command-line style flags from the arguments,
- /// returning a new `Argument` instance containing only non-flag arguments.
- #[must_use]
- pub fn strip_all_flags(mut self) -> Self {
- self.vec.retain(|f| !f.starts_with('-'));
- self
- }
-}
diff --git a/mingling/src/parser/picker.rs b/mingling/src/parser/picker.rs
deleted file mode 100644
index 2f43e8c..0000000
--- a/mingling/src/parser/picker.rs
+++ /dev/null
@@ -1,815 +0,0 @@
-// Doc Not Optimize
-use crate::parser::Argument;
-use mingling_core::{EnumTag, Flag};
-
-#[doc(hidden)]
-pub mod builtin;
-
-#[doc(hidden)]
-pub mod bools;
-
-#[doc(hidden)]
-pub mod path;
-
-/// A builder for extracting values from command-line arguments.
-///
-/// The `Picker` struct holds parsed arguments and provides a fluent interface
-/// to extract values associated with specific flags.
-#[derive(Default)]
-pub struct Picker {
- /// The parsed command-line arguments.
- pub args: Argument,
-}
-
-impl Picker {
- /// Creates a new `Picker` from a value that can be converted into `Argument`.
- pub fn new(args: impl Into<Argument>) -> Self {
- Self { args: args.into() }
- }
-
- /// Extracts a value for the given flag and returns a `Pick1` builder (no route).
- ///
- /// The extracted type `TNext` must implement `Pickable` and `Default`.
- /// If the flag is not present, the default value for `TNext` is used.
- pub fn pick<TNext>(mut self, val: impl Into<Flag>) -> Pick1<TNext>
- where
- TNext: Pickable<Output = TNext> + Default,
- {
- let v = TNext::pick(&mut self.args, val.into()).unwrap_or_default();
- Pick1 {
- args: self.args,
- val_1: v,
- }
- }
-
- /// Extracts a value for the given flag, returning the provided default value if not present,
- /// and returns a `Pick1` builder (no route).
- ///
- /// The extracted type `TNext` must implement `Pickable`.
- /// If the flag is not present, the provided `or` value is used.
- pub fn pick_or<TNext>(mut self, val: impl Into<Flag>, or: impl Into<TNext>) -> Pick1<TNext>
- where
- TNext: Pickable<Output = TNext>,
- {
- let v = TNext::pick(&mut self.args, val.into()).unwrap_or_else(|| or.into());
- Pick1 {
- args: self.args,
- val_1: v,
- }
- }
-
- /// Extracts a value for the given flag, storing the provided route if the flag is not present,
- /// and returns a `PickWithRoute1` builder (with route).
- ///
- /// The extracted type `TNext` must implement `Pickable` and `Default`.
- /// If the flag is not present, the default value for `TNext` is used and the provided `route`
- /// is stored in the returned builder for later error handling.
- pub fn pick_or_route<TNext, R>(
- mut self,
- val: impl Into<Flag>,
- route: R,
- ) -> PickWithRoute1<TNext, R>
- where
- TNext: Pickable<Output = TNext> + Default,
- {
- let Some(v) = TNext::pick(&mut self.args, val.into()) else {
- return PickWithRoute1 {
- args: self.args,
- val_1: TNext::default(),
- route: Some(route),
- };
- };
- PickWithRoute1 {
- args: self.args,
- val_1: v,
- route: None,
- }
- }
-
- /// Extracts a value for the given flag, returning `None` if the flag is not present,
- /// and returns an `Option<Pick1<TNext>>` builder (no route).
- ///
- /// The extracted type `TNext` must implement `Pickable`.
- /// If the flag is not present, `None` is returned.
- pub fn require<TNext>(mut self, val: impl Into<Flag>) -> Option<Pick1<TNext>>
- where
- TNext: Pickable<Output = TNext>,
- {
- let v = TNext::pick(&mut self.args, val.into());
- match v {
- Some(s) => Some(Pick1 {
- args: self.args,
- val_1: s,
- }),
- None => None,
- }
- }
-
- /// Applies an operation to the parsed arguments and returns the modified `Picker`.
- ///
- /// Takes a closure that receives the current `Argument` and returns a new `Argument`.
- /// The returned `Argument` replaces the original arguments in the builder.
- /// This method can be used to modify or transform the parsed arguments before extracting values.
- #[must_use]
- pub fn operate_args<F: FnOnce(Argument) -> Argument>(mut self, operation: F) -> Self {
- self.args = operation(self.args);
- self
- }
-}
-
-impl<T: Into<Argument>> From<T> for Picker {
- fn from(value: T) -> Self {
- Self::new(value)
- }
-}
-
-/// Extracts values from command-line arguments
-///
-/// The `Pickable` trait defines how to extract the value of a specific flag from parsed arguments
-pub trait Pickable {
- /// The output type produced by the extraction operation, must implement the `Default` trait
- type Output: Default;
-
- /// Extracts the value associated with the given flag from the provided arguments
- ///
- /// If the flag exists and the value can be successfully extracted, returns `Some(Output)`;
- /// otherwise returns `None`
- fn pick(args: &mut Argument, flag: Flag) -> Option<Self::Output>;
-}
-
-// Non-routed Pick structs (no R parameter, no route field)
-
-/// Internal macro: generates the struct definition and common methods
-/// (after, `after_or_route`, `operate_args`) for non-routed Pick structs.
-macro_rules! define_pick_struct {
- ($n:ident $final:ident $final_val:ident $route_self:ident $($T:ident $val:ident),+ $(,)?) => {
- #[doc(hidden)]
- pub struct $n<$($T,)+>
- where
- $($T: Pickable,)+
- {
- #[allow(dead_code)]
- args: Argument,
- $(pub $val: $T,)+
- }
-
- impl<$($T,)+> $n<$($T,)+>
- where
- $($T: Pickable,)+
- {
- /// Applies a transformation to the last extracted value.
- ///
- /// Takes a closure that receives the last extracted value and returns a new value of the same type.
- /// The transformed value replaces the original value in the builder.
- /// This method can be used to modify or validate the extracted value before final unpacking.
- #[must_use]
- pub fn after<F>(mut self, mut edit: F) -> Self
- where
- F: FnMut($final) -> $final,
- {
- self.$final_val = edit(self.$final_val);
- self
- }
-
- /// Applies a transformation to the last extracted value, storing a route if the transformation fails.
- ///
- /// Takes a closure that receives a reference to the last extracted value and returns a `Result`.
- /// If the closure returns `Ok(new_value)`, the new value replaces the original value in the builder.
- /// If the closure returns `Err(route)`, the provided `route` is stored in the builder for later error handling.
- /// If a route was already stored from a previous `pick_or_route` call, the existing route is preserved.
- #[must_use]
- pub fn after_or_route<F, R>(mut self, mut edit: F) -> $route_self<$($T,)+ R>
- where
- F: FnMut(&$final) -> Result<$final, R>,
- {
- match edit(&self.$final_val) {
- Ok(new_value) => {
- self.$final_val = new_value;
- $route_self {
- args: self.args,
- $($val: self.$val,)+
- route: None,
- }
- }
- Err(err_route) => {
- $route_self {
- args: self.args,
- $($val: self.$val,)+
- route: Some(err_route),
- }
- }
- }
- }
-
- /// Applies an operation to the parsed arguments and returns the modified builder.
- ///
- /// Takes a closure that receives the current `Argument` and returns a new `Argument`.
- /// The returned `Argument` replaces the original arguments in the builder.
- /// This method can be used to modify or transform the parsed arguments before extracting values.
- #[must_use]
- pub fn operate_args<F: FnOnce(Argument) -> Argument>(mut self, operation: F) -> Self {
- self.args = operation(self.args);
- self
- }
- }
- };
-}
-
-// Pick1 special case (single value)
-
-define_pick_struct! { Pick1 T1 val_1 PickWithRoute1 T1 val_1 }
-
-impl<T1> From<Pick1<T1>> for (T1,)
-where
- T1: Pickable,
-{
- fn from(pick: Pick1<T1>) -> Self {
- (pick.val_1,)
- }
-}
-
-impl<T1> Pick1<T1>
-where
- T1: Pickable,
-{
- /// Unpacks the builder into the extracted value.
- ///
- /// Always returns the value directly since there is no route.
- pub fn unpack(self) -> T1 {
- self.val_1
- }
-}
-
-// Pick2 .. Pick12
-
-macro_rules! impl_pick_from_tuple {
- ($n:ident $($T:ident $val:ident),+) => {
- impl<$($T,)+> From<$n<$($T,)+>> for ($($T,)+)
- where
- $($T: Pickable,)+
- {
- fn from(pick: $n<$($T,)+>) -> Self {
- ($(pick.$val,)+)
- }
- }
- };
-}
-
-macro_rules! impl_pick_unpack_tuple {
- ($n:ident $($T:ident $val:ident),+) => {
- impl<$($T,)+> $n<$($T,)+>
- where
- $($T: Pickable,)+
- {
- /// Unpacks the builder into a tuple of extracted values.
- ///
- /// Always returns the tuple directly since there is no route.
- pub fn unpack(self) -> ($($T,)+) {
- ($(self.$val,)+)
- }
- }
- };
-}
-
-define_pick_struct! { Pick2 T2 val_2 PickWithRoute2 T1 val_1, T2 val_2 }
-impl_pick_from_tuple! { Pick2 T1 val_1, T2 val_2 }
-impl_pick_unpack_tuple! { Pick2 T1 val_1, T2 val_2 }
-
-define_pick_struct! { Pick3 T3 val_3 PickWithRoute3 T1 val_1, T2 val_2, T3 val_3 }
-impl_pick_from_tuple! { Pick3 T1 val_1, T2 val_2, T3 val_3 }
-impl_pick_unpack_tuple! { Pick3 T1 val_1, T2 val_2, T3 val_3 }
-
-define_pick_struct! { Pick4 T4 val_4 PickWithRoute4 T1 val_1, T2 val_2, T3 val_3, T4 val_4 }
-impl_pick_from_tuple! { Pick4 T1 val_1, T2 val_2, T3 val_3, T4 val_4 }
-impl_pick_unpack_tuple! { Pick4 T1 val_1, T2 val_2, T3 val_3, T4 val_4 }
-
-define_pick_struct! { Pick5 T5 val_5 PickWithRoute5 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5 }
-impl_pick_from_tuple! { Pick5 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5 }
-impl_pick_unpack_tuple! { Pick5 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5 }
-
-define_pick_struct! { Pick6 T6 val_6 PickWithRoute6 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6 }
-impl_pick_from_tuple! { Pick6 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6 }
-impl_pick_unpack_tuple! { Pick6 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6 }
-
-define_pick_struct! { Pick7 T7 val_7 PickWithRoute7 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7 }
-impl_pick_from_tuple! { Pick7 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7 }
-impl_pick_unpack_tuple! { Pick7 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7 }
-
-define_pick_struct! { Pick8 T8 val_8 PickWithRoute8 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7, T8 val_8 }
-impl_pick_from_tuple! { Pick8 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7, T8 val_8 }
-impl_pick_unpack_tuple! { Pick8 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7, T8 val_8 }
-
-define_pick_struct! { Pick9 T9 val_9 PickWithRoute9 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7, T8 val_8, T9 val_9 }
-impl_pick_from_tuple! { Pick9 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7, T8 val_8, T9 val_9 }
-impl_pick_unpack_tuple! { Pick9 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7, T8 val_8, T9 val_9 }
-
-define_pick_struct! { Pick10 T10 val_10 PickWithRoute10 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7, T8 val_8, T9 val_9, T10 val_10 }
-impl_pick_from_tuple! { Pick10 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7, T8 val_8, T9 val_9, T10 val_10 }
-impl_pick_unpack_tuple! { Pick10 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7, T8 val_8, T9 val_9, T10 val_10 }
-
-define_pick_struct! { Pick11 T11 val_11 PickWithRoute11 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7, T8 val_8, T9 val_9, T10 val_10, T11 val_11 }
-impl_pick_from_tuple! { Pick11 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7, T8 val_8, T9 val_9, T10 val_10, T11 val_11 }
-impl_pick_unpack_tuple! { Pick11 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7, T8 val_8, T9 val_9, T10 val_10, T11 val_11 }
-
-define_pick_struct! { Pick12 T12 val_12 PickWithRoute12 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7, T8 val_8, T9 val_9, T10 val_10, T11 val_11, T12 val_12 }
-impl_pick_from_tuple! { Pick12 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7, T8 val_8, T9 val_9, T10 val_10, T11 val_11, T12 val_12 }
-impl_pick_unpack_tuple! { Pick12 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7, T8 val_8, T9 val_9, T10 val_10, T11 val_11, T12 val_12 }
-
-// Non-routed Pick chaining methods (pick, pick_or, pick_or_route, require)
-
-#[doc(hidden)]
-macro_rules! impl_pick_next {
- ($n:ident $next:ident $next_val:ident $route_next:ident $($T:ident $val:ident),+) => {
- impl<$($T,)+> $n<$($T,)+>
- where
- $($T: Pickable,)+
- {
- /// Extracts a value for the given flag and returns a `PickN` builder (no route).
- pub fn pick<TNext>(mut self, val: impl Into<mingling_core::Flag>) -> $next<$($T,)+ TNext>
- where
- TNext: Pickable<Output = TNext> + Default,
- {
- let v = TNext::pick(&mut self.args, val.into()).unwrap_or_default();
- $next {
- args: self.args,
- $($val: self.$val,)+
- $next_val: v,
- }
- }
-
- /// Extracts a value for the given flag, returning the provided default value if not present,
- /// and returns a `PickN` builder (no route).
- pub fn pick_or<TNext>(mut self, val: impl Into<mingling_core::Flag>, or: impl Into<TNext>) -> $next<$($T,)+ TNext>
- where
- TNext: Pickable<Output = TNext>,
- {
- let v = TNext::pick(&mut self.args, val.into()).unwrap_or_else(|| or.into());
- $next {
- args: self.args,
- $($val: self.$val,)+
- $next_val: v,
- }
- }
-
- /// Extracts a value for the given flag, storing the provided route if the flag is not present,
- /// and returns a `PickWithRouteN` builder (with route).
- pub fn pick_or_route<TNext, R>(
- mut self,
- val: impl Into<mingling_core::Flag>,
- route: R,
- ) -> $route_next<$($T,)+ TNext, R>
- where
- TNext: Pickable<Output = TNext> + Default,
- {
- let Some(v) = TNext::pick(&mut self.args, val.into()) else {
- return $route_next {
- args: self.args,
- $($val: self.$val,)+
- $next_val: TNext::default(),
- route: Some(route),
- };
- };
- $route_next {
- args: self.args,
- $($val: self.$val,)+
- $next_val: v,
- route: None,
- }
- }
-
- /// Extracts a value for the given flag, returning `None` if the flag is not present,
- /// and returns an `Option<PickN<TNext>>` builder (no route).
- pub fn require<TNext>(mut self, val: impl Into<mingling_core::Flag>) -> Option<$next<$($T,)+ TNext>>
- where
- TNext: Pickable<Output = TNext>,
- {
- let v = TNext::pick(&mut self.args, val.into());
- match v {
- Some(s) => Some($next {
- args: self.args,
- $($val: self.$val,)+
- $next_val: s,
- }),
- None => None,
- }
- }
- }
- };
-}
-
-impl_pick_next! { Pick1 Pick2 val_2 PickWithRoute2 T1 val_1 }
-impl_pick_next! { Pick2 Pick3 val_3 PickWithRoute3 T1 val_1, T2 val_2 }
-impl_pick_next! { Pick3 Pick4 val_4 PickWithRoute4 T1 val_1, T2 val_2, T3 val_3 }
-impl_pick_next! { Pick4 Pick5 val_5 PickWithRoute5 T1 val_1, T2 val_2, T3 val_3, T4 val_4 }
-impl_pick_next! { Pick5 Pick6 val_6 PickWithRoute6 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5 }
-impl_pick_next! { Pick6 Pick7 val_7 PickWithRoute7 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6 }
-impl_pick_next! { Pick7 Pick8 val_8 PickWithRoute8 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7 }
-impl_pick_next! { Pick8 Pick9 val_9 PickWithRoute9 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7, T8 val_8 }
-impl_pick_next! { Pick9 Pick10 val_10 PickWithRoute10 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7, T8 val_8, T9 val_9 }
-impl_pick_next! { Pick10 Pick11 val_11 PickWithRoute11 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7, T8 val_8, T9 val_9, T10 val_10 }
-impl_pick_next! { Pick11 Pick12 val_12 PickWithRoute12 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7, T8 val_8, T9 val_9, T10 val_10, T11 val_11 }
-
-// Routed PickWithRoute structs (with R parameter, route field)
-
-/// Internal macro: generates the routed struct definition and common methods
-/// (after, `after_or_route`, `operate_args`) for `PickWithRoute` structs.
-macro_rules! define_pick_with_route_struct {
- ($n:ident $final:ident $final_val:ident $($T:ident $val:ident),+) => {
- #[doc(hidden)]
- pub struct $n<$($T,)+ R>
- where
- $($T: Pickable,)+
- {
- #[allow(dead_code)]
- args: Argument,
- $(pub $val: $T,)+
- route: Option<R>,
- }
-
- impl<$($T,)+ R> $n<$($T,)+ R>
- where
- $($T: Pickable,)+
- {
- /// Applies a transformation to the last extracted value.
- ///
- /// Takes a closure that receives the last extracted value and returns a new value of the same type.
- /// The transformed value replaces the original value in the builder.
- /// This method can be used to modify or validate the extracted value before final unpacking.
- #[must_use]
- pub fn after<F>(mut self, mut edit: F) -> Self
- where
- F: FnMut($final) -> $final,
- {
- self.$final_val = edit(self.$final_val);
- self
- }
-
- /// Applies a transformation to the last extracted value, storing a route if the transformation fails.
- ///
- /// Takes a closure that receives a reference to the last extracted value and returns a `Result`.
- /// If the closure returns `Ok(new_value)`, the new value replaces the original value in the builder.
- /// If the closure returns `Err(route)`, the provided `route` is stored in the builder for later error handling.
- /// If a route was already stored from a previous `pick_or_route` call, the existing route is preserved.
- #[must_use]
- pub fn after_or_route<F>(mut self, mut edit: F) -> Self
- where
- F: FnMut(&$final) -> Result<$final, R>,
- {
- let value = &self.$final_val;
- match edit(value) {
- Ok(new_value) => {
- self.$final_val = new_value;
- }
- Err(err_route) => {
- let new_route = match self.route {
- Some(existing_route) => Some(existing_route),
- None => Some(err_route),
- };
- self.route = new_route;
- }
- }
- self
- }
-
- /// Applies an operation to the parsed arguments and returns the modified builder.
- ///
- /// Takes a closure that receives the current `Argument` and returns a new `Argument`.
- /// The returned `Argument` replaces the original arguments in the builder.
- /// This method can be used to modify or transform the parsed arguments before extracting values.
- #[must_use]
- pub fn operate_args<F: FnOnce(Argument) -> Argument>(mut self, operation: F) -> Self {
- self.args = operation(self.args);
- self
- }
- }
- };
-}
-
-/// Internal macro: generates `From` impl for routed `PickWithRouteN` into a tuple.
-macro_rules! impl_pick_with_route_from_tuple {
- ($n:ident $($T:ident $val:ident),+) => {
- impl<$($T,)+ R> From<$n<$($T,)+ R>> for ($($T,)+)
- where
- $($T: Pickable,)+
- {
- fn from(pick: $n<$($T,)+ R>) -> Self {
- ($(pick.$val,)+)
- }
- }
- };
-}
-
-/// Internal macro: generates `unpack` and `unpack_directly` for routed `PickWithRouteN` (N >= 2).
-macro_rules! impl_pick_with_route_unpack_tuple {
- ($n:ident $($T:ident $val:ident),+) => {
- impl<$($T,)+ R> $n<$($T,)+ R>
- where
- $($T: Pickable,)+
- {
- /// Unpacks the builder into a tuple of extracted values.
- ///
- /// Returns `Ok((T1, T2, ...))` if no route was stored.
- /// Returns `Err(R)` if a route was stored via `pick_or_route` or `after_or_route`.
- ///
- /// # Errors
- ///
- /// Returns `Err(R)` if a route was stored via `pick_or_route` or `after_or_route`.
- pub fn unpack(self) -> Result<($($T,)+), R> {
- match self.route {
- Some(route) => Err(route),
- None => Ok(($(self.$val,)+)),
- }
- }
-
- /// Unpacks the builder into a tuple of extracted values.
- ///
- /// Returns the tuple of extracted values regardless of route state.
- #[must_use]
- pub fn unpack_directly(self) -> ($($T,)+) {
- ($(self.$val,)+)
- }
- }
- };
-}
-
-// PickWithRoute1 special case (single value)
-
-define_pick_with_route_struct! { PickWithRoute1 T1 val_1 T1 val_1 }
-
-impl<T1, R> From<PickWithRoute1<T1, R>> for (T1,)
-where
- T1: Pickable,
-{
- fn from(pick: PickWithRoute1<T1, R>) -> Self {
- (pick.val_1,)
- }
-}
-
-impl<T1, R> PickWithRoute1<T1, R>
-where
- T1: Pickable,
-{
- /// Unpacks the builder into the extracted value.
- ///
- /// Returns `Ok(T1)` if no route was stored.
- /// Returns `Err(R)` if a route was stored via `pick_or_route` or `after_or_route`.
- ///
- /// # Errors
- ///
- /// Returns `Err(R)` if a route was stored via `pick_or_route` or `after_or_route`.
- pub fn unpack(self) -> Result<T1, R> {
- match self.route {
- Some(route) => Err(route),
- None => Ok(self.val_1),
- }
- }
-
- /// Unpacks the builder into the extracted value.
- ///
- /// Returns the extracted value regardless of route state.
- #[must_use]
- pub fn unpack_directly(self) -> T1 {
- self.val_1
- }
-}
-
-// PickWithRoute2 .. PickWithRoute12
-
-define_pick_with_route_struct! { PickWithRoute2 T2 val_2 T1 val_1, T2 val_2 }
-impl_pick_with_route_from_tuple! { PickWithRoute2 T1 val_1, T2 val_2 }
-impl_pick_with_route_unpack_tuple! { PickWithRoute2 T1 val_1, T2 val_2 }
-
-define_pick_with_route_struct! { PickWithRoute3 T3 val_3 T1 val_1, T2 val_2, T3 val_3 }
-impl_pick_with_route_from_tuple! { PickWithRoute3 T1 val_1, T2 val_2, T3 val_3 }
-impl_pick_with_route_unpack_tuple! { PickWithRoute3 T1 val_1, T2 val_2, T3 val_3 }
-
-define_pick_with_route_struct! { PickWithRoute4 T4 val_4 T1 val_1, T2 val_2, T3 val_3, T4 val_4 }
-impl_pick_with_route_from_tuple! { PickWithRoute4 T1 val_1, T2 val_2, T3 val_3, T4 val_4 }
-impl_pick_with_route_unpack_tuple! { PickWithRoute4 T1 val_1, T2 val_2, T3 val_3, T4 val_4 }
-
-define_pick_with_route_struct! { PickWithRoute5 T5 val_5 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5 }
-impl_pick_with_route_from_tuple! { PickWithRoute5 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5 }
-impl_pick_with_route_unpack_tuple! { PickWithRoute5 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5 }
-
-define_pick_with_route_struct! { PickWithRoute6 T6 val_6 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6 }
-impl_pick_with_route_from_tuple! { PickWithRoute6 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6 }
-impl_pick_with_route_unpack_tuple! { PickWithRoute6 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6 }
-
-define_pick_with_route_struct! { PickWithRoute7 T7 val_7 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7 }
-impl_pick_with_route_from_tuple! { PickWithRoute7 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7 }
-impl_pick_with_route_unpack_tuple! { PickWithRoute7 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7 }
-
-define_pick_with_route_struct! { PickWithRoute8 T8 val_8 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7, T8 val_8 }
-impl_pick_with_route_from_tuple! { PickWithRoute8 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7, T8 val_8 }
-impl_pick_with_route_unpack_tuple! { PickWithRoute8 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7, T8 val_8 }
-
-define_pick_with_route_struct! { PickWithRoute9 T9 val_9 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7, T8 val_8, T9 val_9 }
-impl_pick_with_route_from_tuple! { PickWithRoute9 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7, T8 val_8, T9 val_9 }
-impl_pick_with_route_unpack_tuple! { PickWithRoute9 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7, T8 val_8, T9 val_9 }
-
-define_pick_with_route_struct! { PickWithRoute10 T10 val_10 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7, T8 val_8, T9 val_9, T10 val_10 }
-impl_pick_with_route_from_tuple! { PickWithRoute10 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7, T8 val_8, T9 val_9, T10 val_10 }
-impl_pick_with_route_unpack_tuple! { PickWithRoute10 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7, T8 val_8, T9 val_9, T10 val_10 }
-
-define_pick_with_route_struct! { PickWithRoute11 T11 val_11 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7, T8 val_8, T9 val_9, T10 val_10, T11 val_11 }
-impl_pick_with_route_from_tuple! { PickWithRoute11 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7, T8 val_8, T9 val_9, T10 val_10, T11 val_11 }
-impl_pick_with_route_unpack_tuple! { PickWithRoute11 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7, T8 val_8, T9 val_9, T10 val_10, T11 val_11 }
-
-define_pick_with_route_struct! { PickWithRoute12 T12 val_12 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7, T8 val_8, T9 val_9, T10 val_10, T11 val_11, T12 val_12 }
-impl_pick_with_route_from_tuple! { PickWithRoute12 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7, T8 val_8, T9 val_9, T10 val_10, T11 val_11, T12 val_12 }
-impl_pick_with_route_unpack_tuple! { PickWithRoute12 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7, T8 val_8, T9 val_9, T10 val_10, T11 val_11, T12 val_12 }
-
-// Routed PickWithRoute chaining methods (pick, pick_or, pick_or_route, require)
-
-#[doc(hidden)]
-macro_rules! impl_pick_with_route_next {
- ($n:ident $next:ident $next_val:ident $($T:ident $val:ident),+) => {
- impl<$($T,)+ R> $n<$($T,)+ R>
- where
- $($T: Pickable,)+
- {
- /// Extracts a value for the given flag and returns a `PickWithRouteN` builder.
- pub fn pick<TNext>(mut self, val: impl Into<mingling_core::Flag>) -> $next<$($T,)+ TNext, R>
- where
- TNext: Pickable<Output = TNext> + Default,
- {
- let v = TNext::pick(&mut self.args, val.into()).unwrap_or_default();
- $next {
- args: self.args,
- $($val: self.$val,)+
- $next_val: v,
- route: self.route,
- }
- }
-
- /// Extracts a value for the given flag, returning the provided default value if not present,
- /// and returns a `PickWithRouteN` builder.
- pub fn pick_or<TNext>(mut self, val: impl Into<mingling_core::Flag>, or: impl Into<TNext>) -> $next<$($T,)+ TNext, R>
- where
- TNext: Pickable<Output = TNext>,
- {
- let v = TNext::pick(&mut self.args, val.into()).unwrap_or_else(|| or.into());
- $next {
- args: self.args,
- $($val: self.$val,)+
- $next_val: v,
- route: self.route,
- }
- }
-
- /// Extracts a value for the given flag, storing the provided route if the flag is not present,
- /// and returns a `PickWithRouteN` builder.
- ///
- /// If a route was already stored from a previous `pick_or_route` or `after_or_route` call,
- /// the existing route is preserved and the new `route` parameter is ignored.
- #[allow(clippy::manual_let_else)]
- pub fn pick_or_route<TNext>(mut self, val: impl Into<mingling_core::Flag>, route: R) -> $next<$($T,)+ TNext, R>
- where
- TNext: Pickable<Output = TNext> + Default,
- {
- let v = match TNext::pick(&mut self.args, val.into()) {
- Some(value) => value,
- None => {
- let new_route = match self.route {
- Some(existing_route) => Some(existing_route),
- None => Some(route),
- };
- return $next {
- args: self.args,
- $($val: self.$val,)+
- $next_val: TNext::default(),
- route: new_route,
- };
- }
- };
- $next {
- args: self.args,
- $($val: self.$val,)+
- $next_val: v,
- route: self.route,
- }
- }
-
- /// Extracts a value for the given flag, returning `None` if the flag is not present,
- /// and returns an `Option<PickWithRouteN>` builder.
- pub fn require<TNext>(mut self, val: impl Into<mingling_core::Flag>) -> Option<$next<$($T,)+ TNext, R>>
- where
- TNext: Pickable<Output = TNext>,
- {
- let v = TNext::pick(&mut self.args, val.into());
- match v {
- Some(s) => Some($next {
- args: self.args,
- $($val: self.$val,)+
- $next_val: s,
- route: self.route,
- }),
- None => None,
- }
- }
- }
- };
-}
-
-impl_pick_with_route_next! { PickWithRoute1 PickWithRoute2 val_2 T1 val_1 }
-impl_pick_with_route_next! { PickWithRoute2 PickWithRoute3 val_3 T1 val_1, T2 val_2 }
-impl_pick_with_route_next! { PickWithRoute3 PickWithRoute4 val_4 T1 val_1, T2 val_2, T3 val_3 }
-impl_pick_with_route_next! { PickWithRoute4 PickWithRoute5 val_5 T1 val_1, T2 val_2, T3 val_3, T4 val_4 }
-impl_pick_with_route_next! { PickWithRoute5 PickWithRoute6 val_6 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5 }
-impl_pick_with_route_next! { PickWithRoute6 PickWithRoute7 val_7 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6 }
-impl_pick_with_route_next! { PickWithRoute7 PickWithRoute8 val_8 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7 }
-impl_pick_with_route_next! { PickWithRoute8 PickWithRoute9 val_9 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7, T8 val_8 }
-impl_pick_with_route_next! { PickWithRoute9 PickWithRoute10 val_10 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7, T8 val_8, T9 val_9 }
-impl_pick_with_route_next! { PickWithRoute10 PickWithRoute11 val_11 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7, T8 val_8, T9 val_9, T10 val_10 }
-impl_pick_with_route_next! { PickWithRoute11 PickWithRoute12 val_12 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7, T8 val_8, T9 val_9, T10 val_10, T11 val_11 }
-
-/// Trait for types that can be used with `Pickable` to extract enum values from command-line arguments.
-///
-/// This trait combines `EnumTag` (for building an enum variant from a string name) and `Default`
-/// (for providing a fallback value when the flag is not present).
-///
-/// Types implementing this trait can be used with `Picker::pick`, `Picker::pick_or_route`, and
-/// the chaining `.pick()` methods to extract and parse enum values from command-line arguments.
-pub trait PickableEnum: EnumTag + Default {}
-
-impl<T> Pickable for T
-where
- T: PickableEnum,
-{
- type Output = T;
-
- fn pick(args: &mut Argument, flag: Flag) -> Option<Self::Output> {
- let name = args.pick_argument(flag)?;
- T::build_enum(name)
- }
-}
-
-/// Trait for types that can be converted into a `Picker` to extract values from command-line arguments.
-///
-/// This trait provides a convenient way to convert a value (such as `Vec<String>`, `&[String]`, etc.)
-/// into a `Picker` and immediately start extracting values associated with specific flags.
-pub trait AsPicker
-where
- Self: Into<Vec<String>>,
-{
- /// Converts the value into a `Picker` by first converting it into a `Vec<String>`.
- fn to_picker(self) -> Picker
- where
- Self: Sized,
- Vec<String>: From<Self>,
- {
- let vec: Vec<String> = self.into();
- Picker { args: vec.into() }
- }
-
- /// Extracts a value for the given flag and returns a `Pick1` builder (no route).
- ///
- /// The extracted type `TNext` must implement `Pickable` and `Default`.
- /// If the flag is not present, the default value for `TNext` is used.
- fn pick<TNext>(self, val: impl Into<Flag>) -> Pick1<TNext>
- where
- Self: Sized,
- TNext: Pickable<Output = TNext> + Default,
- {
- let vec: Vec<String> = self.into();
- let picker: Picker = vec.into();
- picker.pick(val)
- }
-
- /// Extracts a value for the given flag, returning the provided default value if not present,
- /// and returns a `Pick1` builder (no route).
- ///
- /// The extracted type `TNext` must implement `Pickable`.
- /// If the flag is not present, the provided `or` value is used.
- fn pick_or<TNext>(self, val: impl Into<Flag>, or: impl Into<TNext>) -> Pick1<TNext>
- where
- TNext: Pickable<Output = TNext>,
- {
- let vec: Vec<String> = self.into();
- let picker: Picker = vec.into();
- picker.pick_or(val, or)
- }
-
- /// Extracts a value for the given flag, storing the provided route if the flag is not present,
- /// and returns a `PickWithRoute1` builder (with route).
- ///
- /// The extracted type `TNext` must implement `Pickable` and `Default`.
- /// If the flag is not present, the default value for `TNext` is used and the provided `route`
- /// is stored in the returned builder for later error handling.
- fn pick_or_route<TNext, R>(self, val: impl Into<Flag>, route: R) -> PickWithRoute1<TNext, R>
- where
- TNext: Pickable<Output = TNext> + Default,
- {
- let vec: Vec<String> = self.into();
- let picker: Picker = vec.into();
- picker.pick_or_route(val, route)
- }
-}
-
-// Implement AsPicker for any type that can be converted into a Vec<String>
-impl<T> AsPicker for T
-where
- T: Sized,
- Vec<String>: From<T>,
-{
-}
diff --git a/mingling/src/parser/picker/bools.rs b/mingling/src/parser/picker/bools.rs
deleted file mode 100644
index bc9fd90..0000000
--- a/mingling/src/parser/picker/bools.rs
+++ /dev/null
@@ -1,143 +0,0 @@
-// Doc Not Optimize
-use crate::parser::Pickable;
-
-/// Represents a boolean-like value with `Yes` and `No` variants.
-///
-/// `Yes` can be parsed from command-line arguments using positive keywords such as `"y"` or `"yes"`,
-/// and defaults to `No`.
-#[derive(Debug, Default)]
-#[repr(u8)]
-pub enum Yes {
- /// The affirmative/positive variant.
- Yes,
- /// The negative/default variant.
- #[default]
- No,
-}
-
-impl From<bool> for Yes {
- fn from(b: bool) -> Self {
- if b { Self::Yes } else { Self::No }
- }
-}
-
-impl From<Yes> for bool {
- fn from(val: Yes) -> Self {
- match val {
- Yes::Yes => true,
- Yes::No => false,
- }
- }
-}
-
-impl std::ops::Deref for Yes {
- type Target = bool;
-
- fn deref(&self) -> &Self::Target {
- static TRUE: bool = true;
- static FALSE: bool = false;
- match self {
- Self::Yes => &TRUE,
- Self::No => &FALSE,
- }
- }
-}
-
-impl Yes {
- #[must_use]
- pub const fn is_yes(&self) -> bool {
- matches!(self, Self::Yes)
- }
-
- #[must_use]
- pub const fn is_no(&self) -> bool {
- matches!(self, Self::No)
- }
-}
-
-impl Pickable for Yes {
- type Output = Self;
-
- fn pick(args: &mut crate::parser::Argument, flag: mingling_core::Flag) -> Option<Self::Output> {
- let value = pick_bool(args, flag, &["y", "yes"]);
- Some(value.into())
- }
-}
-
-/// Represents a boolean-like value with `True` and `False` variants.
-///
-/// `True` can be parsed from command-line arguments using positive keywords such as `"t"` or `"true"`,
-/// and defaults to `False`.
-#[derive(Debug, Default)]
-#[repr(u8)]
-pub enum True {
- /// The affirmative/positive variant.
- True,
- /// The negative/default variant.
- #[default]
- False,
-}
-
-impl From<bool> for True {
- fn from(b: bool) -> Self {
- if b { Self::True } else { Self::False }
- }
-}
-
-impl From<True> for bool {
- fn from(val: True) -> Self {
- match val {
- True::True => true,
- True::False => false,
- }
- }
-}
-
-impl std::ops::Deref for True {
- type Target = bool;
-
- fn deref(&self) -> &Self::Target {
- static TRUE: bool = true;
- static FALSE: bool = false;
- match self {
- Self::True => &TRUE,
- Self::False => &FALSE,
- }
- }
-}
-
-impl True {
- #[must_use]
- pub const fn is_true(&self) -> bool {
- matches!(self, Self::True)
- }
-
- #[must_use]
- pub const fn is_false(&self) -> bool {
- matches!(self, Self::False)
- }
-}
-
-impl Pickable for True {
- type Output = Self;
-
- fn pick(args: &mut crate::parser::Argument, flag: mingling_core::Flag) -> Option<Self::Output> {
- let value = pick_bool(args, flag, &["true", "t"]);
- Some(value.into())
- }
-}
-
-fn pick_bool(
- args: &mut crate::parser::Argument,
- flag: mingling_core::Flag,
- positive: &[&str],
-) -> bool {
- let content = args.pick_argument(flag);
- content.map_or_else(
- || false,
- |content| {
- let s = content.as_str();
- positive.contains(&s)
- },
- )
-}
diff --git a/mingling/src/parser/picker/builtin.rs b/mingling/src/parser/picker/builtin.rs
deleted file mode 100644
index 6f67c78..0000000
--- a/mingling/src/parser/picker/builtin.rs
+++ /dev/null
@@ -1,113 +0,0 @@
-// Doc Not Optimize
-use size::Size;
-
-use crate::parser::{Argument, Pickable};
-
-impl Pickable for String {
- type Output = Self;
-
- fn pick(args: &mut crate::parser::Argument, flag: mingling_core::Flag) -> Option<Self::Output> {
- args.pick_argument(flag)
- }
-}
-
-impl Pickable for Vec<String> {
- type Output = Self;
-
- fn pick(args: &mut crate::parser::Argument, flag: mingling_core::Flag) -> Option<Self::Output> {
- Some(args.pick_arguments(flag))
- }
-}
-
-macro_rules! impl_pickable_for_number {
- ($($t:ty),*) => {
- $(
- impl Pickable for $t {
- type Output = $t;
-
- fn pick(args: &mut crate::parser::Argument, flag: mingling_core::Flag) -> Option<Self::Output> {
- let picked = args.pick_argument(flag)?;
- picked.parse().ok()
- }
- }
-
- impl Pickable for Vec<$t> {
- type Output = Vec<$t>;
-
- fn pick(args: &mut crate::parser::Argument, flag: mingling_core::Flag) -> Option<Self::Output> {
- let picked_vec = args.pick_arguments(flag);
- let mut result = Vec::new();
- for picked in picked_vec {
- if let Ok(parsed) = picked.parse() {
- result.push(parsed);
- } else {
- return None;
- }
- }
- Some(result)
- }
- }
- )*
- };
-}
-
-impl_pickable_for_number!(i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, f32, f64);
-
-impl Pickable for bool {
- type Output = Self;
-
- fn pick(args: &mut crate::parser::Argument, flag: mingling_core::Flag) -> Option<Self::Output> {
- Some(args.pick_flag(flag))
- }
-}
-
-/// Special: parses a size string (e.g. "10MB") into a `usize` representing the number of bytes.
-impl Pickable for usize {
- type Output = Self;
-
- fn pick(args: &mut crate::parser::Argument, flag: mingling_core::Flag) -> Option<Self::Output> {
- let picked = args.pick_argument(flag)?;
- let size_parse = Size::from_str(picked.as_str());
- size_parse.map_or(None, |size| Self::try_from(size.bytes()).ok())
- }
-}
-
-/// Special: parses a comma-separated list of size strings (e.g. "10MB,20KB") into a `Vec<usize>`.
-impl Pickable for Vec<usize> {
- type Output = Self;
-
- fn pick(args: &mut crate::parser::Argument, flag: mingling_core::Flag) -> Option<Self::Output> {
- let picked_vec = args.pick_arguments(flag);
- let mut result = Self::new();
- for picked in picked_vec {
- let size_parse = Size::from_str(picked.as_str());
- match size_parse {
- Ok(size) => result.push(usize::try_from(size.bytes()).unwrap_or(usize::MAX)),
- Err(_) => return None,
- }
- }
- Some(result)
- }
-}
-
-/// Special: dumps the remaining arguments into an `Argument` struct.
-impl Pickable for Argument {
- type Output = Self;
-
- fn pick(
- args: &mut crate::parser::Argument,
- _flag: mingling_core::Flag,
- ) -> Option<Self::Output> {
- Some(args.dump_remains().into())
- }
-}
-
-/// Special: parses a single value of type `T` using the `Pickable` implementation for `T`, and wraps it in an `Option`.
-impl<T: Pickable<Output = T> + Default> Pickable for Option<T> {
- type Output = Self;
-
- fn pick(args: &mut Argument, flag: mingling_core::Flag) -> Option<Self::Output> {
- let r = T::pick(args, flag);
- Some(r)
- }
-}
diff --git a/mingling/src/parser/picker/path.rs b/mingling/src/parser/picker/path.rs
deleted file mode 100644
index 1caecfa..0000000
--- a/mingling/src/parser/picker/path.rs
+++ /dev/null
@@ -1,145 +0,0 @@
-// Doc Not Optimize
-use std::path::{Path, PathBuf};
-
-use crate::parser::Pickable;
-
-mod rule;
-pub use rule::*;
-
-impl Pickable for Vec<PathBuf> {
- type Output = Self;
-
- fn pick(args: &mut crate::parser::Argument, flag: mingling_core::Flag) -> Option<Self::Output> {
- let raw: Vec<String> = args.pick_arguments(flag);
- let paths = raw.into_iter().map(PathBuf::from).collect();
- Some(paths)
- }
-}
-
-impl Pickable for PathBuf {
- type Output = Self;
-
- fn pick(args: &mut crate::parser::Argument, flag: mingling_core::Flag) -> Option<Self::Output> {
- let raw: String = args.pick_argument(flag)?;
- Some(Self::from(raw))
- }
-}
-
-/// Provides path checking methods for [`Vec<PathBuf>`]
-///
-/// This trait automatically provides implementations for `Into<Vec<PathBuf>>`
-pub trait PathsChecker {
- /// Check if all paths in the list satisfy the rule
- fn is_all_passed(&self, rule: &PathCheckRule) -> bool
- where
- Self: Into<Vec<PathBuf>> + Clone,
- {
- check_paths(self.clone(), rule).is_ok()
- }
-
- /// Classify paths into (Passed, Stripped)
- ///
- /// Passed means paths that satisfy the rule, Stripped means paths that do not.
- fn classify(self, rule: &PathCheckRule) -> (Vec<PathBuf>, Vec<PathBuf>)
- where
- Self: Into<Vec<PathBuf>>,
- {
- let paths = self.into();
- let mut passed = Vec::new();
- let mut stripped = Vec::new();
- for path in paths {
- if check_path(&path, rule).is_ok() {
- passed.push(path);
- } else {
- stripped.push(path);
- }
- }
- (passed, stripped)
- }
-
- /// Return paths that satisfy the rule
- fn passed(self, rule: &PathCheckRule) -> Vec<PathBuf>
- where
- Self: Into<Vec<PathBuf>>,
- {
- self.classify(rule).0
- }
-
- /// Return paths that do not satisfy the rule
- fn stripped(self, rule: &PathCheckRule) -> Vec<PathBuf>
- where
- Self: Into<Vec<PathBuf>>,
- {
- self.classify(rule).1
- }
-}
-
-/// Provides path checking methods for [`PathBuf`]
-///
-/// This trait automatically provides implementations for `Into<PathBuf>`
-pub trait PathChecker {
- fn is_passed(&self, rule: &PathCheckRule) -> bool
- where
- Self: Into<PathBuf> + Clone,
- {
- check_path(self.clone(), rule).is_ok()
- }
-}
-
-impl<T: Into<Vec<PathBuf>>> PathsChecker for T {}
-impl<T: Into<PathBuf>> PathChecker for T {}
-
-fn check_paths(path: impl Into<Vec<PathBuf>>, rule: &PathCheckRule) -> Result<(), ()> {
- let paths = path.into();
- for p in &paths {
- check_exist(p, rule)?;
- check_type(p, rule)?;
- }
-
- Ok(())
-}
-
-fn check_path(path: impl Into<PathBuf>, rule: &PathCheckRule) -> Result<(), ()> {
- let p = path.into();
- check_exist(&p, rule)?;
- check_type(&p, rule)?;
-
- Ok(())
-}
-
-fn check_exist(path: &Path, rule: &PathCheckRule) -> Result<(), ()> {
- let Some(exist_check) = &rule.exist_check else {
- return Ok(());
- };
-
- match exist_check {
- PathExistCheck::Exists => bool_to_result(path.exists()),
- PathExistCheck::NotExists => bool_to_result(!path.exists()),
- }
-}
-
-fn check_type(path: &Path, rule: &PathCheckRule) -> Result<(), ()> {
- let Some(type_check) = &rule.type_check else {
- return Ok(());
- };
-
- let is_dir = path.is_dir();
- let is_file = path.is_file();
- let is_symlink = path.is_symlink();
-
- if type_check.allow_dir && is_dir {
- return Ok(());
- }
- if type_check.allow_file && is_file {
- return Ok(());
- }
- if type_check.allow_symlink && is_symlink {
- return Ok(());
- }
-
- Err(())
-}
-
-const fn bool_to_result(b: bool) -> Result<(), ()> {
- if b { Ok(()) } else { Err(()) }
-}
diff --git a/mingling/src/parser/picker/path/rule.rs b/mingling/src/parser/picker/path/rule.rs
deleted file mode 100644
index 5256f35..0000000
--- a/mingling/src/parser/picker/path/rule.rs
+++ /dev/null
@@ -1,231 +0,0 @@
-// Doc Not Optimize
-/// Path check rule
-#[derive(Default)]
-pub struct PathCheckRule {
- pub exist_check: Option<PathExistCheck>,
- pub type_check: Option<PathTypeCheck>,
-}
-
-/// Path existence check
-pub enum PathExistCheck {
- Exists,
- NotExists,
-}
-
-/// Path type check
-pub struct PathTypeCheck {
- /// Whether the path is allowed to be a file
- pub allow_file: bool,
-
- /// Whether the path is allowed to be a directory
- pub allow_dir: bool,
-
- /// Whether the path is allowed to be a symlink
- pub allow_symlink: bool,
-}
-
-impl PathCheckRule {
- /// Creates a new `PathCheckRule` with default values
- #[must_use]
- pub const fn new() -> Self {
- Self {
- exist_check: None,
- type_check: None,
- }
- }
-
- /// Allows the path to be a file
- #[must_use]
- pub const fn allow_file(self) -> Self {
- match self.type_check {
- Some(type_check) => Self {
- type_check: Some(PathTypeCheck {
- allow_file: true,
- allow_dir: type_check.allow_dir,
- allow_symlink: type_check.allow_symlink,
- }),
- ..self
- },
- None => Self {
- type_check: Some(PathTypeCheck {
- allow_file: true,
- allow_dir: false,
- allow_symlink: false,
- }),
- ..self
- },
- }
- }
-
- /// Allows the path to be a directory
- #[must_use]
- pub const fn allow_dir(self) -> Self {
- match self.type_check {
- Some(type_check) => Self {
- type_check: Some(PathTypeCheck {
- allow_file: type_check.allow_file,
- allow_dir: true,
- allow_symlink: type_check.allow_symlink,
- }),
- ..self
- },
- None => Self {
- type_check: Some(PathTypeCheck {
- allow_file: false,
- allow_dir: true,
- allow_symlink: false,
- }),
- ..self
- },
- }
- }
-
- /// Allows the path to be a symlink
- #[must_use]
- pub const fn allow_symlink(self) -> Self {
- match self.type_check {
- Some(type_check) => Self {
- type_check: Some(PathTypeCheck {
- allow_file: type_check.allow_file,
- allow_dir: type_check.allow_dir,
- allow_symlink: true,
- }),
- ..self
- },
- None => Self {
- type_check: Some(PathTypeCheck {
- allow_file: false,
- allow_dir: false,
- allow_symlink: true,
- }),
- ..self
- },
- }
- }
-
- /// Denies the path from being a file
- #[must_use]
- pub const fn deny_file(self) -> Self {
- match self.type_check {
- Some(type_check) => Self {
- type_check: Some(PathTypeCheck {
- allow_file: false,
- allow_dir: type_check.allow_dir,
- allow_symlink: type_check.allow_symlink,
- }),
- ..self
- },
- None => Self {
- type_check: Some(PathTypeCheck {
- allow_file: false,
- allow_dir: true,
- allow_symlink: true,
- }),
- ..self
- },
- }
- }
-
- /// Denies the path from being a directory
- #[must_use]
- pub const fn deny_dir(self) -> Self {
- match self.type_check {
- Some(type_check) => Self {
- type_check: Some(PathTypeCheck {
- allow_file: type_check.allow_file,
- allow_dir: false,
- allow_symlink: type_check.allow_symlink,
- }),
- ..self
- },
- None => Self {
- type_check: Some(PathTypeCheck {
- allow_file: true,
- allow_dir: false,
- allow_symlink: true,
- }),
- ..self
- },
- }
- }
-
- /// Denies the path from being a symlink
- #[must_use]
- pub const fn deny_symlink(self) -> Self {
- match self.type_check {
- Some(type_check) => Self {
- type_check: Some(PathTypeCheck {
- allow_file: type_check.allow_file,
- allow_dir: type_check.allow_dir,
- allow_symlink: false,
- }),
- ..self
- },
- None => Self {
- type_check: Some(PathTypeCheck {
- allow_file: true,
- allow_dir: true,
- allow_symlink: false,
- }),
- ..self
- },
- }
- }
-
- /// Requires the path to be a file (overrides type checks)
- #[must_use]
- pub const fn must_file(self) -> Self {
- Self {
- type_check: Some(PathTypeCheck {
- allow_file: true,
- allow_dir: false,
- allow_symlink: false,
- }),
- ..self
- }
- }
-
- /// Requires the path to be a directory (overrides type checks)
- #[must_use]
- pub const fn must_dir(self) -> Self {
- Self {
- type_check: Some(PathTypeCheck {
- allow_file: false,
- allow_dir: true,
- allow_symlink: false,
- }),
- ..self
- }
- }
-
- /// Requires the path to be a symlink (overrides type checks)
- #[must_use]
- pub const fn must_symlink(self) -> Self {
- Self {
- type_check: Some(PathTypeCheck {
- allow_file: false,
- allow_dir: false,
- allow_symlink: true,
- }),
- ..self
- }
- }
-
- /// Requires the path to exist
- #[must_use]
- pub const fn must_exist(self) -> Self {
- Self {
- exist_check: Some(PathExistCheck::Exists),
- ..self
- }
- }
-
- /// Requires the path to not exist
- #[must_use]
- pub const fn must_not_exist(self) -> Self {
- Self {
- exist_check: Some(PathExistCheck::NotExists),
- ..self
- }
- }
-}
diff --git a/mingling/src/parser/test.rs b/mingling/src/parser/test.rs
deleted file mode 100644
index 172d1da..0000000
--- a/mingling/src/parser/test.rs
+++ /dev/null
@@ -1,731 +0,0 @@
-// Doc Not Optimize
-use crate::parser::picker::bools::{True, Yes};
-use crate::parser::{Argument, Pick1, Picker};
-
-#[test]
-fn test_argument_from_static_str() {
- let arg: Argument = "hello".into();
- assert_eq!(arg.len(), 1);
- assert_eq!(arg[0], "hello");
-}
-
-#[test]
-fn test_argument_from_slice() {
- let arg: Argument = (&["--name", "value"][..]).into();
- assert_eq!(arg.len(), 2);
- assert_eq!(arg[0], "--name");
- assert_eq!(arg[1], "value");
-}
-
-#[test]
-fn test_argument_from_array() {
- let arg: Argument = ["--file", "test.txt"].into();
- assert_eq!(arg.len(), 2);
-}
-
-#[test]
-fn test_argument_from_vec() {
- let arg: Argument = vec!["a".to_string(), "b".to_string()].into();
- assert_eq!(arg.len(), 2);
-}
-
-#[test]
-fn test_argument_default_is_empty() {
- let arg = Argument::default();
- assert!(arg.is_empty());
-}
-
-#[test]
-fn test_pick_argument_with_flag() {
- let mut arg: Argument = vec!["--name", "Alice", "--verbose"].into();
- let value = arg.pick_argument("--name");
- assert_eq!(value, Some("Alice".to_string()));
- // After picking, the flag and its value are removed
- assert_eq!(arg.as_ref(), &["--verbose"]);
-}
-
-#[test]
-fn test_pick_argument_flag_not_found() {
- let mut arg: Argument = vec!["--name", "Alice"].into();
- let value = arg.pick_argument("--missing");
- assert_eq!(value, None);
- // Original args unchanged
- assert_eq!(arg.as_ref(), &["--name", "Alice"]);
-}
-
-#[test]
-fn test_pick_argument_empty() {
- let mut arg: Argument = Argument::default();
- let value = arg.pick_argument("--flag");
- assert_eq!(value, None);
-}
-
-#[test]
-fn test_pick_argument_flag_at_end_no_value() {
- let mut arg: Argument = vec!["--name"].into();
- let value = arg.pick_argument("--name");
- assert_eq!(value, None);
- assert!(arg.is_empty());
-}
-
-#[test]
-fn test_pick_argument_no_flag_positional() {
- let mut arg: Argument = vec!["first", "second", "--flag", "val"].into();
- let value = arg.pick_argument(());
- assert_eq!(value, Some("first".to_string()));
- assert_eq!(arg.as_ref(), &["second", "--flag", "val"]);
-}
-
-#[test]
-fn test_pick_argument_positional_all() {
- let mut arg: Argument = vec!["one", "two", "three"].into();
- let v1 = arg.pick_argument(());
- let v2 = arg.pick_argument(());
- let v3 = arg.pick_argument(());
- let v4 = arg.pick_argument(());
- assert_eq!(v1, Some("one".to_string()));
- assert_eq!(v2, Some("two".to_string()));
- assert_eq!(v3, Some("three".to_string()));
- assert_eq!(v4, None);
-}
-
-#[test]
-fn test_pick_argument_empty_args_no_flag() {
- let mut arg: Argument = Argument::default();
- let value = arg.pick_argument(());
- assert_eq!(value, None);
-}
-
-#[test]
-fn test_pick_argument_with_flag_from_iter() {
- let mut arg: Argument = vec!["-f", "data.txt", "--other"].into();
- let value = arg.pick_argument(&["-f", "--file"][..]);
- assert_eq!(value, Some("data.txt".to_string()));
- assert_eq!(arg.as_ref(), &["--other"]);
-}
-
-#[test]
-fn test_pick_arguments_multiple_values() {
- let mut arg: Argument = vec!["--files", "a.txt", "b.txt", "c.txt", "--other"].into();
- let values = arg.pick_arguments("--files");
- assert_eq!(values, vec!["a.txt", "b.txt", "c.txt"]);
- assert_eq!(arg.as_ref(), &["--other"]);
-}
-
-#[test]
-fn test_pick_arguments_single_value() {
- let mut arg: Argument = vec!["--name", "Alice", "--verbose"].into();
- let values = arg.pick_arguments("--name");
- assert_eq!(values, vec!["Alice"]);
- assert_eq!(arg.as_ref(), &["--verbose"]);
-}
-
-#[test]
-fn test_pick_arguments_no_values() {
- let mut arg: Argument = vec!["--flag", "--other", "val"].into();
- let values = arg.pick_arguments("--flag");
- assert!(values.is_empty());
- assert_eq!(arg.as_ref(), &["--other", "val"]);
-}
-
-#[test]
-fn test_pick_arguments_flag_not_found() {
- let mut arg: Argument = vec!["--name", "Alice"].into();
- let values = arg.pick_arguments("--missing");
- assert!(values.is_empty());
- assert_eq!(arg.as_ref(), &["--name", "Alice"]);
-}
-
-#[test]
-fn test_pick_arguments_stops_at_next_flag() {
- let mut arg: Argument = vec!["--list", "a", "b", "-c", "d", "e"].into();
- let values = arg.pick_arguments("--list");
- assert_eq!(values, vec!["a", "b"]);
- assert_eq!(arg.as_ref(), &["-c", "d", "e"]);
-}
-
-#[test]
-fn test_pick_arguments_empty_flag_positional() {
- let mut arg: Argument = vec!["pos1", "pos2", "--flag", "val"].into();
- let values = arg.pick_arguments(());
- assert_eq!(values, vec!["pos1", "pos2"]);
- assert_eq!(arg.as_ref(), &["--flag", "val"]);
-}
-
-#[test]
-fn test_pick_arguments_empty_args() {
- let mut arg: Argument = Argument::default();
- let values = arg.pick_arguments("--flag");
- assert!(values.is_empty());
-}
-
-#[test]
-fn test_pick_flag_found() {
- let mut arg: Argument = vec!["--verbose", "--name", "Alice"].into();
- let result = arg.pick_flag("--verbose");
- assert!(result);
- assert_eq!(arg.as_ref(), &["--name", "Alice"]);
-}
-
-#[test]
-fn test_pick_flag_not_found() {
- let mut arg: Argument = vec!["--name", "Alice"].into();
- let result = arg.pick_flag("--verbose");
- assert!(!result);
- assert_eq!(arg.as_ref(), &["--name", "Alice"]);
-}
-
-#[test]
-fn test_pick_flag_empty_args() {
- let mut arg: Argument = Argument::default();
- let result = arg.pick_flag("--flag");
- assert!(!result);
-}
-
-#[test]
-fn test_pick_flag_with_flag_iter() {
- let mut arg: Argument = vec!["-h", "--name", "Alice"].into();
- let result = arg.pick_flag(&["-h", "--help"][..]);
- assert!(result);
-}
-
-#[test]
-fn test_pick_flag_second_not_first() {
- let mut arg: Argument = vec!["--name", "Alice"].into();
- let result = arg.pick_flag(&["-h", "--help"][..]);
- assert!(!result);
-}
-
-#[test]
-fn test_pick_flag_positional_yes() {
- let mut arg: Argument = vec!["yes"].into();
- let result = arg.pick_flag(());
- assert!(result);
- assert!(arg.is_empty());
-}
-
-#[test]
-fn test_pick_flag_positional_no() {
- let mut arg: Argument = vec!["no"].into();
- let result = arg.pick_flag(());
- assert!(!result);
-}
-
-#[test]
-fn test_pick_flag_positional_true() {
- let mut arg: Argument = vec!["true"].into();
- let result = arg.pick_flag(());
- assert!(result);
-}
-
-#[test]
-fn test_pick_flag_positional_false() {
- let mut arg: Argument = vec!["false"].into();
- let result = arg.pick_flag(());
- assert!(!result);
-}
-
-#[test]
-fn test_pick_flag_positional_1() {
- let mut arg: Argument = vec!["1"].into();
- let result = arg.pick_flag(());
- assert!(result);
-}
-
-#[test]
-fn test_pick_flag_positional_0() {
- let mut arg: Argument = vec!["0"].into();
- let result = arg.pick_flag(());
- assert!(!result);
-}
-
-#[test]
-fn test_pick_flag_positional_unknown() {
- let mut arg: Argument = vec!["unknown_value"].into();
- let result = arg.pick_flag(());
- assert!(!result);
-}
-
-#[test]
-fn test_pick_flag_positional_case_insensitive_yes() {
- let mut arg: Argument = vec!["YeS"].into();
- let result = arg.pick_flag(());
- assert!(result);
-}
-
-#[test]
-fn test_dump_remains() {
- let mut arg: Argument = vec!["a", "b", "c"].into();
- let remains = arg.dump_remains();
- assert_eq!(remains, vec!["a", "b", "c"]);
- assert!(arg.is_empty());
-}
-
-#[test]
-fn test_dump_remains_empty() {
- let mut arg: Argument = Argument::default();
- let remains = arg.dump_remains();
- assert!(remains.is_empty());
-}
-
-#[test]
-fn test_dump_remains_after_pick() {
- let mut arg: Argument = vec!["--flag", "value", "extra"].into();
- let _ = arg.pick_argument("--flag");
- let remains = arg.dump_remains();
- assert_eq!(remains, vec!["extra"]);
-}
-
-#[test]
-fn test_strip_all_flags() {
- let arg: Argument = vec!["--verbose", "file.txt", "--format", "json"].into();
- let result = arg.strip_all_flags();
- assert_eq!(result.as_ref(), &["file.txt", "json"]);
-}
-
-#[test]
-fn test_strip_all_flags_no_flags() {
- let arg: Argument = vec!["just", "positional", "args"].into();
- let result = arg.strip_all_flags();
- assert_eq!(result.as_ref(), &["just", "positional", "args"]);
-}
-
-#[test]
-fn test_strip_all_flags_all_flags() {
- let arg: Argument = vec!["--a", "-b", "--c"].into();
- let result = arg.strip_all_flags();
- assert!(result.is_empty());
-}
-
-#[test]
-fn test_strip_all_flags_empty() {
- let arg: Argument = Argument::default();
- let result = arg.strip_all_flags();
- assert!(result.is_empty());
-}
-
-#[test]
-fn test_picker_new() {
- let picker = Picker::new(vec!["--name", "Alice"]);
- assert_eq!(picker.args.len(), 2);
-}
-
-#[test]
-fn test_picker_from_trait() {
- let picker: Picker = vec!["--name", "Alice"].into();
- assert_eq!(picker.args.len(), 2);
-}
-
-#[test]
-fn test_picker_pick_string() {
- let result: String = Picker::new(vec!["--name", "Alice"]).pick("--name").unpack();
- assert_eq!(result, "Alice");
-}
-
-#[test]
-fn test_picker_pick_string_default_when_missing() {
- let result: String = Picker::new(vec!["--other", "val"])
- .pick::<String>("--name")
- .unpack();
- assert_eq!(result, "");
-}
-
-#[test]
-fn test_picker_pick_string_default_when_missing_with_or() {
- let result: String = Picker::new(vec!["--other", "val"])
- .pick_or("--name", "default_name")
- .unpack();
- assert_eq!(result, "default_name");
-}
-
-#[test]
-fn test_picker_pick_bool_flag_present() {
- let result: bool = Picker::new(vec!["--verbose", "--name", "Alice"])
- .pick::<bool>("--verbose")
- .unpack();
- assert!(result);
-}
-
-#[test]
-fn test_picker_pick_bool_flag_absent() {
- let result: bool = Picker::new(vec!["--name", "Alice"])
- .pick::<bool>("--verbose")
- .unpack();
- assert!(!result);
-}
-
-#[test]
-fn test_picker_pick_i32() {
- let result: i32 = Picker::new(vec!["--count", "42"]).pick("--count").unpack();
- assert_eq!(result, 42);
-}
-
-#[test]
-fn test_picker_pick_i32_default_zero() {
- let result: i32 = Picker::new(vec!["--other"]).pick::<i32>("--count").unpack();
- assert_eq!(result, 0);
-}
-
-#[test]
-fn test_picker_pick_f64() {
- let result: f64 = Picker::new(vec!["--ratio", "5.16"])
- .pick("--ratio")
- .unpack();
- let expected: f64 = 5.16;
- assert!((result - expected).abs() < 1e-10);
-}
-
-#[test]
-fn test_picker_pick_u64() {
- let result: u64 = Picker::new(vec!["--size", "100"]).pick("--size").unpack();
- assert_eq!(result, 100);
-}
-
-#[test]
-fn test_picker_pick_i32_parse_failure_returns_default() {
- let result: i32 = Picker::new(vec!["--count", "not-a-number"])
- .pick::<i32>("--count")
- .unpack();
- assert_eq!(result, 0);
-}
-
-#[test]
-fn test_picker_pick_usize_bytes() {
- let result: usize = Picker::new(vec!["--limit", "1024"])
- .pick("--limit")
- .unpack();
- assert_eq!(result, 1024);
-}
-
-#[test]
-fn test_picker_pick_usize_kib() {
- let result: usize = Picker::new(vec!["--limit", "1KiB"])
- .pick("--limit")
- .unpack();
- assert_eq!(result, 1024);
-}
-
-#[test]
-fn test_picker_pick_usize_mib() {
- let result: usize = Picker::new(vec!["--limit", "2MiB"])
- .pick("--limit")
- .unpack();
- assert_eq!(result, 2 * 1024 * 1024);
-}
-
-#[test]
-fn test_picker_pick_usize_parse_failure_returns_default() {
- let result: usize = Picker::new(vec!["--limit", "invalid"])
- .pick::<usize>("--limit")
- .unpack();
- assert_eq!(result, 0);
-}
-
-#[test]
-fn test_picker_pick_vec_string() {
- let result: Vec<String> = Picker::new(vec!["--files", "a.txt", "b.txt", "c.txt"])
- .pick("--files")
- .unpack();
- assert_eq!(result, vec!["a.txt", "b.txt", "c.txt"]);
-}
-
-#[test]
-fn test_picker_pick_vec_string_missing() {
- let result: Vec<String> = Picker::new(vec!["--other", "val"])
- .pick::<Vec<String>>("--files")
- .unpack();
- assert!(result.is_empty());
-}
-
-#[test]
-fn test_picker_pick_vec_usize() {
- let result: Vec<usize> = Picker::new(vec!["--sizes", "100", "1KiB", "2MiB"])
- .pick("--sizes")
- .unpack();
- assert_eq!(result, vec![100, 1024, 2 * 1024 * 1024]);
-}
-
-#[test]
-fn test_picker_pick_vec_i32() {
- let result: Vec<i32> = Picker::new(vec!["--nums", "10", "20", "30"])
- .pick("--nums")
- .unpack();
- assert_eq!(result, vec![10, 20, 30]);
-}
-
-#[test]
-fn test_picker_pick_yes_yes() {
- let result: Yes = Picker::new(vec!["--flag", "y"]).pick("--flag").unpack();
- assert!(result.is_yes());
- assert!(*result);
-}
-
-#[test]
-fn test_picker_pick_yes_no() {
- let result: Yes = Picker::new(vec!["--flag", "no"]).pick("--flag").unpack();
- assert!(result.is_no());
- assert!(!*result);
-}
-
-#[test]
-fn test_picker_pick_yes_default_no() {
- let result: Yes = Picker::new(vec!["--other"]).pick::<Yes>("--flag").unpack();
- assert!(result.is_no());
-}
-
-#[test]
-fn test_picker_pick_true_true() {
- let result: True = Picker::new(vec!["--flag", "true"]).pick("--flag").unpack();
- assert!(result.is_true());
- assert!(*result);
-}
-
-#[test]
-fn test_picker_pick_true_false() {
- let result: True = Picker::new(vec!["--flag", "anything"])
- .pick("--flag")
- .unpack();
- assert!(result.is_false());
- assert!(!*result);
-}
-
-#[test]
-fn test_picker_pick_true_default_false() {
- let result: True = Picker::new(vec!["--other"]).pick::<True>("--flag").unpack();
- assert!(result.is_false());
-}
-
-#[test]
-fn test_picker_pick_or_fallback() {
- let result: String = Picker::new(vec!["--other", "val"])
- .pick_or("--name", "fallback")
- .unpack();
- assert_eq!(result, "fallback");
-}
-
-#[test]
-fn test_picker_pick_or_existing() {
- let result: String = Picker::new(vec!["--name", "Alice"])
- .pick_or("--name", "fallback")
- .unpack();
- assert_eq!(result, "Alice");
-}
-
-#[test]
-fn test_picker_pick_or_numeric_fallback() {
- let result: i32 = Picker::new(vec!["--other"]).pick_or("--count", 99).unpack();
- assert_eq!(result, 99);
-}
-
-#[test]
-fn test_picker_pick_or_route_present() {
- let result = Picker::new(vec!["--name", "Alice"])
- .pick_or_route::<String, _>("--name", "missing_name")
- .unpack();
- assert_eq!(result, Ok("Alice".to_string()));
-}
-
-#[test]
-fn test_picker_pick_or_route_missing() {
- let result = Picker::new(vec!["--other"])
- .pick_or_route::<String, _>("--name", "missing_name")
- .unpack();
- assert_eq!(result, Err("missing_name"));
-}
-
-#[test]
-fn test_picker_require_present() {
- let result: Option<String> = Picker::new(vec!["--name", "Alice"])
- .require::<String>("--name")
- .map(super::picker::Pick1::unpack);
- assert_eq!(result, Some("Alice".to_string()));
-}
-
-#[test]
-fn test_picker_require_missing() {
- let result: Option<Pick1<String>> = Picker::new(vec!["--other"]).require::<String>("--name");
- assert!(result.is_none());
-}
-
-#[test]
-fn test_picker_chaining_two_values() {
- let (name, count): (String, i32) = Picker::new(vec!["--name", "Alice", "--count", "42"])
- .pick::<String>("--name")
- .pick::<i32>("--count")
- .unpack();
- assert_eq!(name, "Alice");
- assert_eq!(count, 42);
-}
-
-#[test]
-fn test_picker_chaining_three_values() {
- let (_name, _verbose, count): (String, bool, i32) =
- Picker::new(vec!["--name", "Alice", "--count", "42", "--verbose"])
- .pick::<String>("--name")
- .pick::<bool>("--verbose")
- .pick::<i32>("--count")
- .unpack();
- assert_eq!(count, 42);
-}
-
-#[test]
-fn test_picker_chaining_with_pick_or() {
- let (name, count): (String, i32) = Picker::new(vec!["--name", "Alice"])
- .pick::<String>("--name")
- .pick_or("--count", 10)
- .unpack();
- assert_eq!(name, "Alice");
- assert_eq!(count, 10);
-}
-
-#[test]
-fn test_picker_chaining_with_mixed_flag_styles() {
- let (name, verbose): (String, bool) = Picker::new(vec!["-n", "Bob", "--verbose"])
- .pick::<String>("-n")
- .pick::<bool>("--verbose")
- .unpack();
- assert_eq!(name, "Bob");
- assert!(verbose);
-}
-
-#[test]
-fn test_pick_after_modification() {
- let result: String = Picker::new(vec!["--name", " Alice "])
- .pick::<String>("--name")
- .after(|s| s.trim().to_string())
- .unpack();
- assert_eq!(result, "Alice");
-}
-
-#[test]
-fn test_pick_after_chained() {
- let (name, count): (String, i32) = Picker::new(vec!["--name", "alice", "--count", "7"])
- .pick::<String>("--name")
- .after(|s| s.to_uppercase())
- .pick::<i32>("--count")
- .after(|n| n * 2)
- .unpack();
- assert_eq!(name, "ALICE");
- assert_eq!(count, 14);
-}
-
-#[test]
-fn test_pick_after_or_route_ok() {
- let result = Picker::new(vec!["--name", "Alice"])
- .pick::<String>("--name")
- .after_or_route(|s| {
- if s.len() > 3 {
- Ok(s.clone())
- } else {
- Err("too_short")
- }
- })
- .unpack();
- assert_eq!(result, Ok("Alice".to_string()));
-}
-
-#[test]
-fn test_pick_after_or_route_err() {
- let result = Picker::new(vec!["--name", "Ab"])
- .pick::<String>("--name")
- .after_or_route(|s| {
- if s.len() > 3 {
- Ok(s.clone())
- } else {
- Err("too_short")
- }
- })
- .unpack();
- assert_eq!(result, Err("too_short"));
-}
-
-#[test]
-fn test_pick_with_route_unpack_ok() {
- let result = Picker::new(vec!["--name", "Alice"])
- .pick_or_route::<String, _>("--name", "error")
- .unpack();
- assert_eq!(result, Ok("Alice".to_string()));
-}
-
-#[test]
-fn test_pick_with_route_unpack_err() {
- let result: Result<String, &str> = Picker::new(vec!["--other"])
- .pick_or_route::<String, _>("--name", "missing")
- .unpack();
- assert_eq!(result, Err("missing"));
-}
-
-#[test]
-fn test_pick_with_route_unpack_directly() {
- let result: String = Picker::new(vec!["--other"])
- .pick_or_route::<String, _>("--name", "fallback_in_route")
- .unpack_directly();
- // When route is set, unpack_directly returns the default value (empty string for String)
- assert_eq!(result, "");
-}
-
-#[test]
-fn test_pick_with_route_chaining_present() {
- let result = Picker::new(vec!["--name", "Alice", "--count", "42"])
- .pick_or_route::<String, _>("--name", "err_name")
- .pick::<i32>("--count")
- .unpack();
- assert_eq!(result, Ok(("Alice".to_string(), 42)));
-}
-
-#[test]
-fn test_pick_with_route_chaining_missing_first_route_propagates() {
- let result = Picker::new(vec!["--count", "42"])
- .pick_or_route::<String, _>("--name", "err_name")
- .pick::<i32>("--count")
- .unpack();
- assert_eq!(result, Err("err_name"));
-}
-
-#[test]
-fn test_pick_with_route_chaining_pick_or_route_second_missing() {
- let result = Picker::new(vec!["--name", "Alice"])
- .pick_or_route::<String, _>("--name", "err_name")
- .pick_or_route::<i32>("--count", "err_count")
- .unpack();
- assert_eq!(result, Err("err_count"));
-}
-
-#[test]
-fn test_pick_with_route_after_or_route_preserves_existing_route() {
- let result = Picker::new(vec!["--other"])
- .pick_or_route::<String, _>("--name", "missing_name")
- .after_or_route(|_s: &String| {
- // This won't be called because route is already set, but let's see behavior
- Ok("should_not_matter".to_string())
- })
- .unpack();
- assert_eq!(result, Err("missing_name"));
-}
-
-#[test]
-fn test_picker_operate_args_filter() {
- let result: String = Picker::new(vec!["--name", "Alice", "--verbose"])
- .operate_args(Argument::strip_all_flags)
- .pick_or("--name", "fallback_name")
- .unpack();
- // After stripping flags, "--name" and "--verbose" are gone, "Alice" is a positional arg.
- // But --name with a value won't be present as a flag, so it falls back to positional.
- // Actually, strip_all_flags removes anything starting with '-'.
- // So "--name" is removed, and "Alice" remains as a positional argument.
- // When we try to pick "--name", it won't find it, so we get the fallback.
- assert_eq!(result, "fallback_name");
-}
-
-#[test]
-fn test_picker_operate_args_transform() {
- let result: Vec<String> = Picker::new(vec!["--files", "a.txt", "b.txt", "c.txt"])
- .operate_args(|mut args| {
- // Add an extra file
- args.push("d.txt".to_string());
- args
- })
- .pick::<Vec<String>>("--files")
- .unpack();
- assert_eq!(result, vec!["a.txt", "b.txt", "c.txt", "d.txt"]);
-}