From 4421fb2794f2af292f8781e7d12ae002a3f10a9b Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Mon, 30 Mar 2026 17:26:03 +0800 Subject: Add argument parser module with picker API --- mingling/Cargo.lock | 15 ++++- mingling/Cargo.toml | 13 +++- mingling/src/lib.rs | 6 +- mingling/src/parser.rs | 5 ++ mingling/src/parser/args.rs | 95 ++++++++++++++++++++++++++++ mingling/src/parser/picker.rs | 116 ++++++++++++++++++++++++++++++++++ mingling/src/parser/picker/builtin.rs | 53 ++++++++++++++++ 7 files changed, 296 insertions(+), 7 deletions(-) create mode 100644 mingling/src/parser.rs create mode 100644 mingling/src/parser/args.rs create mode 100644 mingling/src/parser/picker.rs create mode 100644 mingling/src/parser/picker/builtin.rs (limited to 'mingling') diff --git a/mingling/Cargo.lock b/mingling/Cargo.lock index c8ed514..2f1fae9 100644 --- a/mingling/Cargo.lock +++ b/mingling/Cargo.lock @@ -55,12 +55,15 @@ dependencies = [ name = "mingling" version = "0.1.1" dependencies = [ + "mingling", "mingling_core", + "serde", + "size", ] [[package]] name = "mingling_core" -version = "0.1.0" +version = "0.1.1" dependencies = [ "just_fmt", "mingling_macros", @@ -71,9 +74,9 @@ dependencies = [ [[package]] name = "mingling_macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3790886e4504e486d8c963988e5780212ccc05c5d03a5d27fa2ba67e1dd3f13b" +checksum = "5f1f57fd20f1072939938b779684534d566d2d81104a43a32bf06a377e72bee9" dependencies = [ "just_fmt", "once_cell", @@ -201,6 +204,12 @@ dependencies = [ "libc", ] +[[package]] +name = "size" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b6709c7b6754dca1311b3c73e79fcce40dd414c782c66d88e8823030093b02b" + [[package]] name = "smallvec" version = "1.15.1" diff --git a/mingling/Cargo.toml b/mingling/Cargo.toml index 4d13d0b..c9f0116 100644 --- a/mingling/Cargo.toml +++ b/mingling/Cargo.toml @@ -10,11 +10,18 @@ keywords = ["cli", "framework", "procedural", "subcommand", "command-line"] categories = ["command-line-interface"] repository = "https://github.com/catilgrass/mingling" +[dev-dependencies] +mingling = { path = ".", features = ["full"] } + [features] default = ["mingling_core/default"] -full = ["mingling_core/full"] +full = ["mingling_core/full", "general_renderer", "macros", "parser"] +general_renderer = ["mingling_core/general_renderer", "dep:serde"] macros = ["mingling_core/macros"] -general_renderer = ["mingling_core/general_renderer"] + +parser = ["dep:size"] [dependencies] -mingling_core = { version = "0.1.0", default-features = false } +mingling_core = { path = "../mingling_core", default-features = false } +serde = { version = "1.0", features = ["derive"], optional = true } +size = { version = "0.5", optional = true } diff --git a/mingling/src/lib.rs b/mingling/src/lib.rs index d85b16d..19c2db9 100644 --- a/mingling/src/lib.rs +++ b/mingling/src/lib.rs @@ -1,2 +1,6 @@ // Re-Export Core lib -pub use mingling_core::*; +pub use mingling::*; +pub use mingling_core as mingling; + +#[cfg(feature = "parser")] +pub mod parser; diff --git a/mingling/src/parser.rs b/mingling/src/parser.rs new file mode 100644 index 0000000..be6f9b2 --- /dev/null +++ b/mingling/src/parser.rs @@ -0,0 +1,5 @@ +mod args; +pub use crate::parser::args::*; + +mod picker; +pub use crate::parser::picker::*; diff --git a/mingling/src/parser/args.rs b/mingling/src/parser/args.rs new file mode 100644 index 0000000..e659feb --- /dev/null +++ b/mingling/src/parser/args.rs @@ -0,0 +1,95 @@ +use mingling_core::{Flag, special_argument, special_flag}; + +pub struct Argument { + vec: Vec, +} + +impl From<&'static str> for Argument { + fn from(s: &'static str) -> Self { + Argument { + vec: vec![s.to_string()], + } + } +} + +impl From<&'static [&'static str]> for Argument { + fn from(slice: &'static [&'static str]) -> Self { + Argument { + vec: slice.iter().map(|&s| s.to_string()).collect(), + } + } +} + +impl From<[&'static str; N]> for Argument { + fn from(slice: [&'static str; N]) -> Self { + Argument { + vec: slice.iter().map(|&s| s.to_string()).collect(), + } + } +} + +impl From<&'static [&'static str; N]> for Argument { + fn from(slice: &'static [&'static str; N]) -> Self { + Argument { + vec: slice.iter().map(|&s| s.to_string()).collect(), + } + } +} + +impl From> for Argument { + fn from(vec: Vec) -> Self { + Argument { vec } + } +} + +impl AsRef<[String]> for Argument { + fn as_ref(&self) -> &[String] { + &self.vec + } +} + +impl std::ops::Deref for Argument { + type Target = Vec; + + 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 { + /// Extracts argument (with value) from arguments + pub fn pick_argument(&mut self, flag: F) -> Option + where + F: Into, + { + let flag: Flag = flag.into(); + for argument in flag.iter() { + let value = special_argument!(self.vec, argument); + if value.is_some() { + return value; + } + } + None + } + + /// Extracts flags from arguments + pub fn pick_flag(&mut self, flag: F) -> bool + where + F: Into, + { + let flag: Flag = flag.into(); + for argument in flag.iter() { + let enabled = special_flag!(self.vec, argument); + if enabled { + return enabled; + } + } + false + } +} diff --git a/mingling/src/parser/picker.rs b/mingling/src/parser/picker.rs new file mode 100644 index 0000000..dc8b8da --- /dev/null +++ b/mingling/src/parser/picker.rs @@ -0,0 +1,116 @@ +use crate::parser::Argument; +use mingling_core::Flag; + +pub mod builtin; + +#[doc(hidden)] +pub struct Picker { + pub args: Argument, +} + +impl Picker { + pub fn new(args: impl Into) -> Picker { + Picker { args: args.into() } + } + + pub fn pick(mut self, val: impl Into) -> Pick1 + where + TNext: Pickable + Default, + { + let v = TNext::pick(&mut self.args, val.into()).unwrap_or_default(); + Pick1 { + args: self.args, + val_1: v, + } + } +} + +impl> From for Picker { + fn from(value: T) -> Self { + Picker::new(value) + } +} + +pub trait Pickable { + type Output: Default; + fn pick(args: &mut Argument, flag: Flag) -> Option; +} + +#[doc(hidden)] +macro_rules! define_pick_structs { + ($n:tt $($T:ident $val:ident),+) => { + #[doc(hidden)] + pub struct $n<$($T),+> + where + $($T: Pickable,)+ + { + #[allow(dead_code)] + args: Argument, + $(pub $val: $T,)+ + } + + impl<$($T),+> From<$n<$($T),+>> for ($($T,)+) + where + $($T: Pickable,)+ + { + fn from(pick: $n<$($T),+>) -> Self { + ($(pick.$val,)+) + } + } + + impl<$($T),+> $n<$($T),+> + where + $($T: Pickable,)+ + { + pub fn unpack(self) -> ($($T,)+) { + ($(self.$val,)+) + } + } + }; +} + +#[doc(hidden)] +macro_rules! impl_pick_structs { + ($n:ident $next:ident $next_val:ident $($T:ident $val:ident),+) => { + impl<$($T),+> $n<$($T),+> + where + $($T: Pickable,)+ + { + pub fn pick(mut self, val: impl Into) -> $next<$($T,)+ TNext> + where + TNext: Pickable + Default, + { + let v = TNext::pick(&mut self.args, val.into()).unwrap_or_default(); + $next { + args: self.args, + $($val: self.$val,)+ + $next_val: v, + } + } + } + }; +} + +define_pick_structs! { Pick1 T1 val_1 } +define_pick_structs! { Pick2 T1 val_1, T2 val_2 } +define_pick_structs! { Pick3 T1 val_1, T2 val_2, T3 val_3 } +define_pick_structs! { Pick4 T1 val_1, T2 val_2, T3 val_3, T4 val_4 } +define_pick_structs! { Pick5 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5 } +define_pick_structs! { Pick6 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6 } +define_pick_structs! { Pick7 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6, T7 val_7 } +define_pick_structs! { 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_structs! { 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_structs! { 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_structs! { 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_structs! { 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_structs! { Pick1 Pick2 val_2 T1 val_1 } +impl_pick_structs! { Pick2 Pick3 val_3 T1 val_1, T2 val_2 } +impl_pick_structs! { Pick3 Pick4 val_4 T1 val_1, T2 val_2, T3 val_3 } +impl_pick_structs! { Pick4 Pick5 val_5 T1 val_1, T2 val_2, T3 val_3, T4 val_4 } +impl_pick_structs! { Pick5 Pick6 val_6 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5 } +impl_pick_structs! { Pick6 Pick7 val_7 T1 val_1, T2 val_2, T3 val_3, T4 val_4, T5 val_5, T6 val_6 } +impl_pick_structs! { Pick7 Pick8 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_structs! { Pick8 Pick9 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_structs! { Pick9 Pick10 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_structs! { Pick10 Pick11 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_structs! { Pick11 Pick12 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 } diff --git a/mingling/src/parser/picker/builtin.rs b/mingling/src/parser/picker/builtin.rs new file mode 100644 index 0000000..0b1ce08 --- /dev/null +++ b/mingling/src/parser/picker/builtin.rs @@ -0,0 +1,53 @@ +use size::Size; + +use crate::parser::Pickable; + +impl Pickable for String { + type Output = String; + + fn pick(args: &mut crate::parser::Argument, flag: mingling_core::Flag) -> Option { + args.pick_argument(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 { + let Some(picked) = args.pick_argument(flag) else { + return None; + }; + picked.parse().ok() + } + } + )* + }; +} + +impl_pickable_for_number!(i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, f32, f64); + +impl Pickable for bool { + type Output = bool; + + fn pick(args: &mut crate::parser::Argument, flag: mingling_core::Flag) -> Option { + Some(args.pick_flag(flag)) + } +} + +impl Pickable for usize { + type Output = usize; + + fn pick(args: &mut crate::parser::Argument, flag: mingling_core::Flag) -> Option { + let Some(picked) = args.pick_argument(flag) else { + return None; + }; + let size_parse = Size::from_str(picked.as_str()); + match size_parse { + Ok(size) => Some(size.bytes() as usize), + Err(_) => None, + } + } +} -- cgit