diff options
Diffstat (limited to 'mingling')
| -rw-r--r-- | mingling/Cargo.toml | 2 | ||||
| -rw-r--r-- | mingling/src/constants.rs | 3 | ||||
| -rw-r--r-- | mingling/src/constants/picker.rs | 16 | ||||
| -rw-r--r-- | mingling/src/example_docs.rs | 245 | ||||
| -rw-r--r-- | mingling/src/lib.rs | 27 | ||||
| -rw-r--r-- | mingling/src/picker/entry_picker.rs | 6 |
6 files changed, 275 insertions, 24 deletions
diff --git a/mingling/Cargo.toml b/mingling/Cargo.toml index adf900f..2509029 100644 --- a/mingling/Cargo.toml +++ b/mingling/Cargo.toml @@ -50,7 +50,7 @@ dispatch_tree = ["mingling_core/dispatch_tree", "mingling_macros/dispatch_tree"] repl = ["mingling_core/repl", "mingling_macros/repl"] comp = ["mingling_core/comp", "mingling_macros/comp"] parser = ["dep:size"] -picker = ["dep:arg-picker", "arg-picker/mingling_support"] +picker = ["mingling_core/picker", "dep:arg-picker", "arg-picker/mingling_support"] pathf = ["mingling_core/pathf", "mingling_macros/pathf"] structural_renderer = [ diff --git a/mingling/src/constants.rs b/mingling/src/constants.rs index 65d02e8..75f1c73 100644 --- a/mingling/src/constants.rs +++ b/mingling/src/constants.rs @@ -4,5 +4,8 @@ mod picker; #[cfg(feature = "picker")] pub use picker::*; +#[cfg(feature = "picker")] +pub use arg_picker::consts::*; + mod exit_codes; pub use exit_codes::*; diff --git a/mingling/src/constants/picker.rs b/mingling/src/constants/picker.rs index f2a448b..d98dcab 100644 --- a/mingling/src/constants/picker.rs +++ b/mingling/src/constants/picker.rs @@ -1,20 +1,6 @@ +use arg_picker::{PickerArg, value::Flag}; use std::marker::PhantomData; -use arg_picker::{PickerArg, PickerArgs, value::Flag}; - -/// Remaining positional arguments (anything not consumed as an option). -/// - `full`: `[]` (empty — not triggered by any `--` prefix). -/// - `short`: (none) -/// - `positional`: `false` (this is a meta‑argument that collects everything left). -/// This constant is used internally to access any leftover arguments after -/// all defined flags/options have been processed. -pub const REMAINS: PickerArg<PickerArgs> = PickerArg::<PickerArgs> { - full: &[], - short: None, - positional: false, - internal_type: PhantomData, -}; - /// Help flag: display usage information. /// - `full`: `["help"]` /// - `short`: `'h'` diff --git a/mingling/src/example_docs.rs b/mingling/src/example_docs.rs index fad287f..bdefb5a 100644 --- a/mingling/src/example_docs.rs +++ b/mingling/src/example_docs.rs @@ -124,6 +124,251 @@ /// } /// ``` 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. +/// +/// Run: +/// ```bash +/// cargo run --manifest-path examples/example-argument-picker/Cargo.toml --quiet -- calc 1 + 1 +/// cargo run --manifest-path examples/example-argument-picker/Cargo.toml --quiet -- calc 7 * 7 +/// cargo run --manifest-path examples/example-argument-picker/Cargo.toml --quiet -- calc +/// cargo run --manifest-path examples/example-argument-picker/Cargo.toml --quiet -- calc 1 +/// cargo run --manifest-path examples/example-argument-picker/Cargo.toml --quiet -- calc 1 + +/// cargo run --manifest-path examples/example-argument-picker/Cargo.toml --quiet -- calc 4 / 3 +/// cargo run --manifest-path examples/example-argument-picker/Cargo.toml --quiet -- calc 4 / 3 --round +/// ``` +/// +/// Output: +/// ```plaintext +/// Result: 2 +/// Result: 49 +/// Error: First number (number_a) was not provided. +/// Error: Operator was not provided. +/// Error: Second number (number_b) was not provided. +/// Result: 1.3333334 +/// Result: 1 +/// ``` +/// +/// Source code (./Cargo.toml) +/// ```toml +/// [package] +/// name = "example-argument-picker" +/// version = "0.1.0" +/// edition = "2024" +/// +/// [dependencies.mingling] +/// path = "../../mingling" +/// +/// # Enable `picker` features +/// features = ["picker", "extra_macros"] +/// +/// [workspace] +/// ``` +/// +/// Source code (./src/main.rs) +/// ```ignore +/// use mingling::{ +/// consts::REMAINS, +/// macros::route, +/// picker::{ +/// IntoPicker, PickerArgResult, SinglePickable, +/// parselib::{ParserStyle, UNIX_STYLE}, +/// value::Flag, +/// }, +/// prelude::*, +/// }; +/// +/// // --------- IMPORTANT --------- +/// // Use picker::BasicProgramSetup instead of the original BasicProgramSetup +/// // It uses arg-picker to rewrite the logic of the original BasicProgramSetup +/// use mingling::setup::picker::BasicProgramSetup; +/// +/// // --------- IMPORTANT --------- +/// +/// dispatcher!("calc", CMDCalculate => EntryCalculate); +/// +/// pack_err!(ErrorNumberANotProvided); +/// pack_err!(ErrorNumberBNotProvided); +/// pack_err!(ErrorNumberOperatorNotProvided); +/// pack_err!(ErrorDivisionByZero); +/// +/// pack!(StateAdd = (f32, f32)); +/// pack!(StateSubtract = (f32, f32)); +/// pack!(StateMultiply = (f32, f32)); +/// pack!(StateDivide = (f32, f32)); +/// +/// pack!(ResultNumber = f32); +/// +/// #[derive(Grouped)] +/// struct StateCalculate { +/// number_a: f32, +/// operator: Operator, +/// number_b: f32, +/// } +/// +/// #[derive(Debug, PartialEq, Eq)] +/// enum Operator { +/// Plus, +/// Dash, +/// Slash, +/// Star, +/// } +/// +/// // --------- IMPORTANT --------- +/// // Define SinglePickable for type Operator +/// // This allows the type to be picked as an argument +/// impl SinglePickable for Operator { +/// fn pick_single(str: Option<&str>) -> PickerArgResult<Self> { +/// let Some(str) = str else { +/// return PickerArgResult::NotFound; +/// }; +/// let op = match str.chars().next() { +/// Some('+') => Operator::Plus, +/// Some('-') => Operator::Dash, +/// Some('*') => Operator::Star, +/// Some('/') => Operator::Slash, +/// _ => return PickerArgResult::NotFound, +/// }; +/// PickerArgResult::Parsed(op) +/// } +/// } +/// // --------- IMPORTANT --------- +/// +/// #[derive(Default, Clone)] +/// struct ResNumberDisplaySetting { +/// round: bool, +/// } +/// +/// fn main() { +/// let mut program = ThisProgram::new(); +/// +/// // Use ParserStyle to manage the arg-picker theme +/// ParserStyle::set_global_style(&UNIX_STYLE); +/// +/// // Enable picker::BasicProgramSetup +/// program.with_setup(BasicProgramSetup); +/// +/// // --------- IMPORTANT --------- +/// // Pre-process global arguments before executing commands +/// let (round, args) = program +/// .take_args() +/// // Use arg![round: Flag] to indicate the `--round` | `-R` flag +/// // | +/// // vvvvvvvvvvvvvvvv +/// .pick(&arg![round: Flag, 'R']) +/// // Use REMAINS to extract remaining arguments +/// // | +/// // vvvvvvvv +/// .pick(&REMAINS) +/// // Since Flag and REMAINS will not fail to parse, +/// // we can safely unwrap here +/// .unwrap(); +/// program.replace_args(args.into()); +/// +/// program.with_resource(ResNumberDisplaySetting { round: *round }); +/// // --------- IMPORTANT --------- +/// +/// program.with_dispatcher(CMDCalculate); +/// program.exec_and_exit(); +/// } +/// +/// #[chain] +/// fn handle_calc(args: EntryCalculate) -> Next { +/// // --------- IMPORTANT --------- +/// let (number_a, operator, number_b) = route!( +/// // Use the arg! macro to define a positional argument of type f32 +/// // | +/// // vvvvvvvvvv +/// args.pick_or_route(&arg![f32], || ErrorNumberANotProvided::default().to_chain()) +/// .pick_or_route(&arg![Operator], || { +/// ErrorNumberOperatorNotProvided::default().to_chain() +/// }) // Returns a routable type when not found or fails to parse +/// // | +/// // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +/// .pick_or_route(&arg![f32], || ErrorNumberBNotProvided::default().to_chain()) +/// // Use `to_result` to parse arguments +/// // and convert to Result<(Tuple, ...), Route> type +/// .to_result() +/// ); +/// // --------- IMPORTANT --------- +/// +/// if operator == Operator::Slash && number_b == 0. { +/// return ErrorDivisionByZero::default().to_chain(); +/// } +/// +/// StateCalculate { +/// number_a, +/// operator, +/// number_b, +/// } +/// .to_chain() +/// } +/// +/// #[chain] +/// fn handle_state_calculate(state: StateCalculate) -> Next { +/// match (state.operator, state.number_a, state.number_b) { +/// (Operator::Plus, a, b) => StateAdd::new((a, b)).to_chain(), +/// (Operator::Dash, a, b) => StateSubtract::new((a, b)).to_chain(), +/// (Operator::Slash, a, b) => StateDivide::new((a, b)).to_chain(), +/// (Operator::Star, a, b) => StateMultiply::new((a, b)).to_chain(), +/// } +/// } +/// +/// #[chain] +/// fn handle_state_add(state_add: StateAdd) -> ResultNumber { +/// let (a, b) = state_add.inner; +/// ResultNumber::new(a + b) +/// } +/// +/// #[chain] +/// fn handle_state_subtract(state_subtract: StateSubtract) -> ResultNumber { +/// let (a, b) = state_subtract.inner; +/// ResultNumber::new(a - b) +/// } +/// +/// #[chain] +/// fn handle_state_multiply(state_multiply: StateMultiply) -> ResultNumber { +/// let (a, b) = state_multiply.inner; +/// ResultNumber::new(a * b) +/// } +/// +/// #[chain] +/// fn handle_state_divide(state_divide: StateDivide) -> ResultNumber { +/// let (a, b) = state_divide.inner; +/// ResultNumber::new(a / b) +/// } +/// +/// #[renderer] +/// fn render_result_number(result: ResultNumber, setting: &ResNumberDisplaySetting) -> String { +/// let round = setting.round; +/// let result = if round { result.round() } else { result.inner }; +/// format!("Result: {}", result) +/// } +/// +/// #[renderer] +/// fn render_error_division_by_zero(_: ErrorDivisionByZero) -> String { +/// "Error: Division by zero is not allowed!".to_string() +/// } +/// +/// #[renderer] +/// fn render_error_number_a_not_provided(_: ErrorNumberANotProvided) -> String { +/// "Error: First number (number_a) was not provided.".to_string() +/// } +/// +/// #[renderer] +/// fn render_error_number_b_not_provided(_: ErrorNumberBNotProvided) -> String { +/// "Error: Second number (number_b) was not provided.".to_string() +/// } +/// +/// #[renderer] +/// fn render_error_number_operator_not_provided(_: ErrorNumberOperatorNotProvided) -> String { +/// "Error: Operator was not provided.".to_string() +/// } +/// +/// gen_program!(); +/// ``` +pub mod example_argument_picker {} /// Example Async Runtime Support /// /// > This example shows how to drive an async runtime using the `async` feature diff --git a/mingling/src/lib.rs b/mingling/src/lib.rs index f4bc9dc..b6c9a5a 100644 --- a/mingling/src/lib.rs +++ b/mingling/src/lib.rs @@ -42,6 +42,8 @@ pub mod macros { /// New Parser provided by the `picker` feature #[cfg(feature = "picker")] pub use arg_picker::macros::*; + /// `#[buffer]` - Wraps a unit-returning function to produce a `RenderResult`. + pub use mingling_macros::buffer; /// `#[chain]` - Used to generate a struct implementing the `Chain` trait via a method pub use mingling_macros::chain; /// `#[completion(EntryType)]` - Used to generate completion entry @@ -91,6 +93,12 @@ pub mod macros { /// `#[program_setup]` - Used to generate program setup #[cfg(feature = "extra_macros")] pub use mingling_macros::program_setup; + /// `r_print!` - Prints text to a `RenderResult` buffer (without newline). + /// See the macro documentation for implicit vs. explicit buffer usage. + pub use mingling_macros::r_print; + /// `r_println!` - Prints text to a `RenderResult` buffer (with newline). + /// See the macro documentation for implicit vs. explicit buffer usage. + pub use mingling_macros::r_println; #[doc(hidden)] pub use mingling_macros::register_chain; #[doc(hidden)] @@ -106,6 +114,10 @@ pub mod macros { /// `route! { /* ... */ }` - Used to generate a route that either returns a successful result or early returns an error. #[cfg(feature = "extra_macros")] pub use mingling_macros::route; + /// `#[routeify]` - An extension attribute macro that transforms `expr?` into `route!(expr)`. + /// Can be used standalone or as a chain/renderer extension: `#[chain(routeify, ...)]`. + #[cfg(feature = "extra_macros")] + pub use mingling_macros::routeify; /// `suggest! { "hello", "bye" }` - Used to generate suggestions #[cfg(feature = "comp")] pub use mingling_macros::suggest; @@ -171,12 +183,15 @@ pub mod res; /// use mingling::prelude::*; /// ``` pub mod prelude { - /// Re-export of the `Grouped` derive macro for grouping types. + /// Re-export of the `Grouped` trait #[cfg(feature = "core")] pub use crate::Grouped; /// Re-export of the `RenderResult` struct for outputting rendering result #[cfg(feature = "core")] pub use crate::RenderResult; + /// Re-export of the `Routable` trait + #[cfg(feature = "core")] + pub use crate::Routable; /// Re-export of the `chain` macro for defining a chain of commands. #[cfg(feature = "macros")] pub use crate::macros::chain; @@ -208,6 +223,12 @@ pub mod prelude { /// Like `pack!` but also marks the type for structured output #[cfg(all(feature = "macros", feature = "structural_renderer"))] pub use mingling_macros::pack_structural; + /// `r_print!` - Prints text to a `RenderResult` buffer (without newline). + /// See the macro documentation for implicit vs. explicit buffer usage. + pub use mingling_macros::r_print; + /// `r_println!` - Prints text to a `RenderResult` buffer (with newline). + /// See the macro documentation for implicit vs. explicit buffer usage. + pub use mingling_macros::r_println; /// Re-export of the `completion` macro for generating completion entries. #[cfg(all(feature = "macros", feature = "comp"))] @@ -222,8 +243,4 @@ pub mod prelude { #[cfg(feature = "picker")] pub use crate::picker::EntryPicker; - - /// Used to enable the `writeln!` macro for `RenderResult` - #[cfg(feature = "core")] - pub use std::io::Write; } diff --git a/mingling/src/picker/entry_picker.rs b/mingling/src/picker/entry_picker.rs index 7364c50..d9fb37c 100644 --- a/mingling/src/picker/entry_picker.rs +++ b/mingling/src/picker/entry_picker.rs @@ -35,7 +35,7 @@ pub trait EntryPicker<'a, This> { ) -> PickerPattern1<'a, Next, ChainProcess<This>> where Self: Sized, - Next: Pickable<'a> + Default + Sized, + Next: Pickable<'a> + Sized, { let picker = Self::to_picker(self); Picker::build_pattern1(picker.into_args(), arg.into(), None) @@ -60,7 +60,7 @@ pub trait EntryPicker<'a, This> { ) -> PickerPattern1<'a, Next, ChainProcess<This>> where Self: Sized, - Next: Pickable<'a> + Default + Sized, + Next: Pickable<'a> + Sized, F: FnMut() -> Next + 'static, { self.pick(arg).or(func) @@ -107,7 +107,7 @@ pub trait EntryPicker<'a, This> { ) -> PickerPattern1<'a, Next, ChainProcess<This>> where Self: Sized, - Next: Pickable<'a> + Default + Sized, + Next: Pickable<'a> + Sized, F: FnMut() -> ChainProcess<This> + 'static, { self.pick(arg).or_route(func) |
