diff options
Diffstat (limited to 'examples')
| -rw-r--r-- | examples/example-argument-picker/Cargo.lock | 94 | ||||
| -rw-r--r-- | examples/example-argument-picker/Cargo.toml | 12 | ||||
| -rw-r--r-- | examples/example-argument-picker/page.toml | 10 | ||||
| -rw-r--r-- | examples/example-argument-picker/src/main.rs | 225 | ||||
| -rw-r--r-- | examples/example-clap-binding/src/main.rs | 8 | ||||
| -rw-r--r-- | examples/example-custom-pickable/src/main.rs | 8 | ||||
| -rw-r--r-- | examples/example-enum-tag/src/main.rs | 4 | ||||
| -rw-r--r-- | examples/example-outside-type/src/main.rs | 2 | ||||
| -rw-r--r-- | examples/example-structural-renderer/src/main.rs | 14 | ||||
| -rw-r--r-- | examples/full-todolist/src/todolist.rs | 4 | ||||
| -rw-r--r-- | examples/test-examples.toml | 35 |
11 files changed, 396 insertions, 20 deletions
diff --git a/examples/example-argument-picker/Cargo.lock b/examples/example-argument-picker/Cargo.lock new file mode 100644 index 0000000..9a9baa4 --- /dev/null +++ b/examples/example-argument-picker/Cargo.lock @@ -0,0 +1,94 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "arg-picker" +version = "0.1.0" +dependencies = [ + "arg-picker-macros", + "just_fmt", +] + +[[package]] +name = "arg-picker-macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "example-argument-picker" +version = "0.1.0" +dependencies = [ + "mingling", +] + +[[package]] +name = "just_fmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6170dccbc3ea15dfb7f2da964097f814aba1dd8f746d4ffc56f33245c38e6d96" + +[[package]] +name = "mingling" +version = "0.3.0" +dependencies = [ + "arg-picker", + "mingling_core", + "mingling_macros", +] + +[[package]] +name = "mingling_core" +version = "0.3.0" +dependencies = [ + "just_fmt", +] + +[[package]] +name = "mingling_macros" +version = "0.3.0" +dependencies = [ + "just_fmt", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" diff --git a/examples/example-argument-picker/Cargo.toml b/examples/example-argument-picker/Cargo.toml new file mode 100644 index 0000000..686b95b --- /dev/null +++ b/examples/example-argument-picker/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "example-argument-picker" +version = "0.1.0" +edition = "2024" + +[dependencies.mingling] +path = "../../mingling" + +# Enable `picker` features +features = ["picker", "extra_macros"] + +[workspace] diff --git a/examples/example-argument-picker/page.toml b/examples/example-argument-picker/page.toml new file mode 100644 index 0000000..563bccc --- /dev/null +++ b/examples/example-argument-picker/page.toml @@ -0,0 +1,10 @@ +[example] +id = "example-argument-picker" +name = "Argument Picker" +icon = "📋" +category = "parsing" +desc = """ +Demonstrates how to use Mingling's `picker` feature and `Picker` to extract typed arguments from the command line. +""" +tags = ["arg-picker", "SinglePickable"] +files = ["src/main.rs", "Cargo.toml"] diff --git a/examples/example-argument-picker/src/main.rs b/examples/example-argument-picker/src/main.rs new file mode 100644 index 0000000..7fcc5db --- /dev/null +++ b/examples/example-argument-picker/src/main.rs @@ -0,0 +1,225 @@ +//! 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 +//! ``` + +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!(); diff --git a/examples/example-clap-binding/src/main.rs b/examples/example-clap-binding/src/main.rs index d99f8d1..0f839c8 100644 --- a/examples/example-clap-binding/src/main.rs +++ b/examples/example-clap-binding/src/main.rs @@ -38,7 +38,7 @@ //! For more information, try '--help'. //! ``` -use mingling::{Groupped, macros::dispatcher_clap, prelude::*, setup::BasicProgramSetup}; +use mingling::{Grouped, macros::dispatcher_clap, prelude::*, setup::BasicProgramSetup}; use std::io::Write; fn main() { @@ -67,10 +67,10 @@ fn main() { // Implement Clap Parser, and bind to Dispatcher // _______________________________ Default trait, provides fallback on parse failure // / ______________________ clap::Parser, parsing logic implemented by Clap -// | / ________ Implement mingling::Groupped +// | / ________ Implement mingling::Grouped // | | / to ensure Mingling can recognize the type -// vvvvvvv vvvvvvvvvvvv vvvvvvvv -#[derive(Default, clap::Parser, Groupped)] +// vvvvvvv vvvvvvvvvvvv vvvvvvv +#[derive(Default, clap::Parser, Grouped)] #[dispatcher_clap( "greet", CMDGreet, // Bind EntryGreet to "greet" command help = true, // Generate clap help for EntryGreet diff --git a/examples/example-custom-pickable/src/main.rs b/examples/example-custom-pickable/src/main.rs index d203815..b163278 100644 --- a/examples/example-custom-pickable/src/main.rs +++ b/examples/example-custom-pickable/src/main.rs @@ -14,15 +14,15 @@ //! Failed to parse address //! ``` -use mingling::{macros::route, parser::Pickable, prelude::*, Groupped}; +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 Groupped derive macro registers an ID for this type +// / ________ The Grouped derive macro registers an ID for this type // | / Mingling uses this ID to identify the type -// vvvvvvv vvvvvvvv -#[derive(Debug, Default, Clone, Groupped)] +// vvvvvvv vvvvvvv +#[derive(Debug, Default, Clone, Grouped)] pub struct Address { pub ip: [u8; 4], pub port: u16, diff --git a/examples/example-enum-tag/src/main.rs b/examples/example-enum-tag/src/main.rs index 91c5358..b57511d 100644 --- a/examples/example-enum-tag/src/main.rs +++ b/examples/example-enum-tag/src/main.rs @@ -16,7 +16,7 @@ //! ``` use mingling::{ - macros::suggest_enum, parser::PickableEnum, prelude::*, EnumTag, Groupped, ShellContext, + macros::suggest_enum, parser::PickableEnum, prelude::*, EnumTag, Grouped, ShellContext, Suggest, }; use std::io::Write; @@ -25,7 +25,7 @@ use std::io::Write; // ________ adds metadata to the enum, enabling it to: // / 1. Be used by the `suggest_enum!(Enum)` macro under the `comp` feature for autocompletion // vvvvvvv 2. Implement the `PickableEnum` trait -#[derive(Debug, Default, EnumTag, Groupped)] +#[derive(Debug, Default, EnumTag, Grouped)] pub enum ProgrammingLanguages { #[enum_desc("An efficient and flexible compiled language widely used for system programming")] C, diff --git a/examples/example-outside-type/src/main.rs b/examples/example-outside-type/src/main.rs index 925878a..3159d19 100644 --- a/examples/example-outside-type/src/main.rs +++ b/examples/example-outside-type/src/main.rs @@ -71,7 +71,7 @@ fn render_number(num: ParsedNumber) -> RenderResult { /// Renderer for parse errors — using the outside `ParseIntError` type. /// /// The `ParseIntError` type is registered via `group!` above, so it implements -/// `Groupped<ThisProgram>` and can be used directly in a `#[renderer]` function. +/// `Grouped<ThisProgram>` and can be used directly in a `#[renderer]` function. #[renderer] fn render_parse_error(err: ParseIntError) -> RenderResult { let mut render_result = RenderResult::new(); diff --git a/examples/example-structural-renderer/src/main.rs b/examples/example-structural-renderer/src/main.rs index 13c4c84..070e75d 100644 --- a/examples/example-structural-renderer/src/main.rs +++ b/examples/example-structural-renderer/src/main.rs @@ -18,7 +18,7 @@ //! ``` use mingling::prelude::*; -use mingling::{Groupped, StructuralData, parser::Picker, setup::StructuralRendererSetup}; +use mingling::{parser::Picker, setup::StructuralRendererSetup, Grouped, StructuralData}; use serde::Serialize; use std::io::Write; @@ -35,12 +35,12 @@ fn main() { // --------- IMPORTANT --------- // For beautiful output structure, do not use `pack!` to wrap the types that need to be output. // Instead, manually implement -// __________________________________ Mark as structured data so it can be rendered -// / ____________________ Implement serde::Serialize -// | / _________ Implement mingling::Groupped -// | | / to ensure Mingling can recognize the type -// vvvvvvvvvvvv vvvvvvvvv vvvvvvvv -#[derive(StructuralData, Serialize, Groupped)] +// ____________________________________ Mark as structured data so it can be rendered +// / ____________________ Implement serde::Serialize +// | / _________ Implement mingling::Grouped +// | | / to ensure Mingling can recognize the type +// vvvvvvvvvvvv vvvvvvvvv vvvvvvv +#[derive(StructuralData, Serialize, Grouped)] struct Info { #[serde(rename = "member_name")] name: String, diff --git a/examples/full-todolist/src/todolist.rs b/examples/full-todolist/src/todolist.rs index 71338c3..d3582e3 100644 --- a/examples/full-todolist/src/todolist.rs +++ b/examples/full-todolist/src/todolist.rs @@ -1,13 +1,13 @@ //! Data structures, read and write logic for the todo list -use mingling::{Groupped, RenderResult, macros::renderer}; +use mingling::{Grouped, RenderResult, macros::renderer}; use serde::{Deserialize, Serialize}; use std::io::Write; use std::{env::current_dir, path::PathBuf}; use crate::ResProgramFlags; -#[derive(Debug, Serialize, Deserialize, Clone, Default, Groupped)] +#[derive(Debug, Serialize, Deserialize, Clone, Default, Grouped)] pub struct ResTodoList { pub items: Vec<Todo>, } diff --git a/examples/test-examples.toml b/examples/test-examples.toml index d2fe602..e450919 100644 --- a/examples/test-examples.toml +++ b/examples/test-examples.toml @@ -277,3 +277,38 @@ expect.result = "Hello, Alice!" command = "hello" expect.exit-code = 0 expect.result = "Hello, World!" + +[[test.example-argument-picker]] +command = "calc 1 + 1" +expect.exit-code = 0 +expect.result = "Result: 2" + +[[test.example-argument-picker]] +command = "calc 7 * 7" +expect.exit-code = 0 +expect.result = "Result: 49" + +[[test.example-argument-picker]] +command = "calc" +expect.exit-code = 0 +expect.result = "Error: First number (number_a) was not provided." + +[[test.example-argument-picker]] +command = "calc 1" +expect.exit-code = 0 +expect.result = "Error: Operator was not provided." + +[[test.example-argument-picker]] +command = "calc 1 +" +expect.exit-code = 0 +expect.result = "Error: Second number (number_b) was not provided." + +[[test.example-argument-picker]] +command = "calc 4 / 3" +expect.exit-code = 0 +expect.result = "Result: 1.3333334" + +[[test.example-argument-picker]] +command = "calc 4 / 3 --round" +expect.exit-code = 0 +expect.result = "Result: 1" |
