summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-03-30 17:26:03 +0800
committer魏曹先生 <1992414357@qq.com>2026-03-30 17:26:03 +0800
commit4421fb2794f2af292f8781e7d12ae002a3f10a9b (patch)
tree31a358dd80a7e1d3855f4a6281eaf5e010bfb429
parentc9e3676e0e2353dc09471c783a3a263dee0fdcb1 (diff)
Add argument parser module with picker API
-rw-r--r--mingling/Cargo.lock15
-rw-r--r--mingling/Cargo.toml13
-rw-r--r--mingling/src/lib.rs6
-rw-r--r--mingling/src/parser.rs5
-rw-r--r--mingling/src/parser/args.rs95
-rw-r--r--mingling/src/parser/picker.rs116
-rw-r--r--mingling/src/parser/picker/builtin.rs53
-rw-r--r--mingling_core/Cargo.lock2
-rw-r--r--mingling_core/Cargo.toml2
-rw-r--r--mingling_core/src/program/flag.rs2
10 files changed, 300 insertions, 9 deletions
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",
@@ -202,6 +205,12 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
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<String>,
+}
+
+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<const N: usize> From<[&'static str; N]> for Argument {
+ fn from(slice: [&'static str; N]) -> Self {
+ Argument {
+ 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 {
+ Argument {
+ vec: slice.iter().map(|&s| s.to_string()).collect(),
+ }
+ }
+}
+
+impl From<Vec<String>> for Argument {
+ fn from(vec: Vec<String>) -> Self {
+ Argument { 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 {
+ /// Extracts argument (with value) from arguments
+ pub fn pick_argument<F>(&mut self, flag: F) -> Option<String>
+ where
+ F: Into<Flag>,
+ {
+ 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<F>(&mut self, flag: F) -> bool
+ where
+ F: Into<Flag>,
+ {
+ 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<Argument>) -> Picker {
+ Picker { args: args.into() }
+ }
+
+ 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,
+ }
+ }
+}
+
+impl<T: Into<Argument>> From<T> for Picker {
+ fn from(value: T) -> Self {
+ Picker::new(value)
+ }
+}
+
+pub trait Pickable {
+ type Output: Default;
+ fn pick(args: &mut Argument, flag: Flag) -> Option<Self::Output>;
+}
+
+#[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<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,
+ }
+ }
+ }
+ };
+}
+
+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<Self::Output> {
+ 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<Self::Output> {
+ 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<Self::Output> {
+ Some(args.pick_flag(flag))
+ }
+}
+
+impl Pickable for usize {
+ type Output = usize;
+
+ fn pick(args: &mut crate::parser::Argument, flag: mingling_core::Flag) -> Option<Self::Output> {
+ 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,
+ }
+ }
+}
diff --git a/mingling_core/Cargo.lock b/mingling_core/Cargo.lock
index 8cd28b4..cd02598 100644
--- a/mingling_core/Cargo.lock
+++ b/mingling_core/Cargo.lock
@@ -53,7 +53,7 @@ dependencies = [
[[package]]
name = "mingling_core"
-version = "0.1.0"
+version = "0.1.1"
dependencies = [
"just_fmt",
"mingling_macros",
diff --git a/mingling_core/Cargo.toml b/mingling_core/Cargo.toml
index b532ab1..d60c36e 100644
--- a/mingling_core/Cargo.toml
+++ b/mingling_core/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "mingling_core"
-version = "0.1.0"
+version = "0.1.1"
edition = "2024"
license = "MIT OR Apache-2.0"
description = "Core of the mingling library"
diff --git a/mingling_core/src/program/flag.rs b/mingling_core/src/program/flag.rs
index 3a678be..81126e5 100644
--- a/mingling_core/src/program/flag.rs
+++ b/mingling_core/src/program/flag.rs
@@ -48,6 +48,7 @@ impl std::ops::Deref for Flag {
}
}
+#[macro_export]
macro_rules! special_flag {
($args:expr, $flag:expr) => {{
let flag = $flag;
@@ -57,6 +58,7 @@ macro_rules! special_flag {
}};
}
+#[macro_export]
macro_rules! special_argument {
($args:expr, $flag:expr) => {{
let flag = $flag;