diff options
Diffstat (limited to 'mingling')
| -rw-r--r-- | mingling/src/example_docs.rs | 382 | ||||
| -rw-r--r-- | mingling/src/example_docs.rs.tmpl | 18 | ||||
| -rw-r--r-- | mingling/src/lib.rs | 162 |
3 files changed, 404 insertions, 158 deletions
diff --git a/mingling/src/example_docs.rs b/mingling/src/example_docs.rs new file mode 100644 index 0000000..ec8162e --- /dev/null +++ b/mingling/src/example_docs.rs @@ -0,0 +1,382 @@ +// Auto generated + +/// `Mingling` Example - Basic +/// +/// # How to Run +/// ```bash +/// cargo run --manifest-path ./examples/example-basic/Cargo.toml -- hello World +/// ``` +/// +/// Cargo.toml +/// ``` +/// [package] +/// name = "example-basic" +/// version = "0.0.1" +/// edition = "2024" +/// +/// [dependencies] +/// mingling = { path = "../../mingling" } +/// tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] } +/// ``` +/// +/// main.rs +/// ```rust +/// use mingling::{ +/// macros::{chain, dispatcher, gen_program, pack, r_println, renderer}, +/// marker::NextProcess, +/// }; +/// +/// // Define dispatcher `HelloCommand`, directing subcommand "hello" to `HelloEntry` +/// dispatcher!("hello", HelloCommand => HelloEntry); +/// +/// #[tokio::main] +/// async fn main() { +/// // Create program +/// let mut program = ThisProgram::new(); +/// +/// // Add dispatcher `HelloCommand` +/// program.with_dispatcher(HelloCommand); +/// +/// // Run program +/// program.exec().await; +/// } +/// +/// // Register wrapper type `Hello`, setting inner to `String` +/// pack!(Hello = String); +/// +/// // Register chain to `ThisProgram`, handling logic from `HelloEntry` +/// #[chain] +/// async fn parse_name(prev: HelloEntry) -> NextProcess { +/// // Extract string from `HelloEntry` as argument +/// let name = prev.first().cloned().unwrap_or_else(|| "World".to_string()); +/// +/// // Build `Hello` type and route to renderer +/// Hello::new(name).to_render() +/// } +/// +/// // Register renderer to `ThisProgram`, handling rendering of `Hello` +/// #[renderer] +/// fn render_hello_who(prev: Hello) { +/// // Print message +/// r_println!("Hello, {}!", *prev); +/// +/// // Program ends here +/// } +/// +/// // Generate program, default is `ThisProgram` +/// gen_program!(); +/// ``` +pub mod example_basic {} +/// `Mingling` Example - Completion +/// +/// # How to Deploy +/// 1. Enable the `comp` feature +/// ```toml +/// mingling = { version = "0.1.5", features = [ +/// "comp", // Enable this feature +/// "parser" +/// ] } +/// ``` +/// +/// 2. Write `build.rs` to generate completion scripts at compile time +/// ```rust +/// use mingling::build::{build_comp_scripts, build_comp_scripts_with_bin_name}; +/// fn main() { +/// // Generate completion scripts for the current program +/// build_comp_scripts().unwrap(); +/// +/// // Or specify a specific name +/// // build_comp_scripts_with_bin_name("your_bin").unwrap(); +/// } +/// ``` +/// +/// 3. Write `main.rs`, adding completion logic for your command entry point +/// 4. Execute `cargo install --path ./`, then run the corresponding completion script in your shell +/// +/// Cargo.toml +/// ``` +/// [package] +/// name = "example-completion" +/// version = "0.0.1" +/// edition = "2024" +/// +/// [dependencies] +/// mingling = { path = "../../mingling", features = ["comp", "parser"] } +/// tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] } +/// ``` +/// +/// main.rs +/// ```rust +/// use mingling::{ +/// AnyOutput, Groupped, ShellContext, Suggest, +/// macros::{chain, completion, dispatcher, gen_program, r_println, renderer, suggest}, +/// marker::NextProcess, +/// parser::{Pickable, Picker}, +/// }; +/// +/// // Define dispatcher `FruitCommand`, directing subcommand "fruit" to `FruitEntry` +/// dispatcher!("fruit", FruitCommand => FruitEntry); +/// +/// #[completion(FruitEntry)] +/// fn comp_fruit_command(ctx: &ShellContext) -> Suggest { +/// if ctx.current_word.starts_with("-") { +/// return suggest! { +/// "--name": "Fruit name", +/// "--type": "Fruit type" +/// }; +/// } +/// if ctx.previous_word == "--name" { +/// return suggest!(); +/// } +/// if ctx.previous_word == "--type" { +/// return suggest! { +/// "apple", "banana" +/// }; +/// } +/// return suggest!(); +/// } +/// +/// #[tokio::main] +/// async fn main() { +/// let mut program = ThisProgram::new(); +/// program.with_dispatcher(CompletionDispatcher); // Add completion dispatcher +/// program.with_dispatcher(FruitCommand); +/// program.exec().await; +/// } +/// +/// #[derive(Groupped)] +/// struct FruitInfo { +/// name: String, +/// fruit_type: FruitType, +/// } +/// +/// #[derive(Default, Debug)] +/// enum FruitType { +/// #[default] +/// Apple, +/// Banana, +/// Other(String), +/// } +/// +/// impl Pickable for FruitType { +/// type Output = FruitType; +/// +/// fn pick(args: &mut mingling::parser::Argument, flag: mingling::Flag) -> Option<Self::Output> { +/// let name = args.pick_argument(flag); +/// match name { +/// Some(name) => match name.as_str() { +/// "apple" => Some(FruitType::Apple), +/// "banana" => Some(FruitType::Banana), +/// other => Some(FruitType::Other(other.to_string())), +/// }, +/// None => None, +/// } +/// } +/// } +/// +/// #[chain] +/// async fn parse_fruit_info(prev: FruitEntry) -> NextProcess { +/// let picker = Picker::<ThisProgram>::from(prev.inner); +/// let (fruit_name, fruit_type) = picker.pick("--name").pick("--type").unpack_directly(); +/// let info = FruitInfo { +/// name: fruit_name, +/// fruit_type, +/// }; +/// AnyOutput::new(info).route_renderer() +/// } +/// +/// #[renderer] +/// fn render_fruit(prev: FruitInfo) { +/// if let FruitType::Other(other) = prev.fruit_type { +/// r_println!("Fruit name: {}, Type: {:?} (Unknown)", prev.name, other); +/// } else { +/// r_println!("Fruit name: {}, Type: {:?}", prev.name, prev.fruit_type); +/// } +/// } +/// +/// gen_program!(); +/// ``` +pub mod example_completion {} +/// `Mingling` Example - General Renderer +/// +/// ## Step1 - Enable Feature +/// Enable the `general_renderer` feature for mingling in `Cargo.toml` +/// ```toml +/// [dependencies] +/// mingling = { version = "...", features = ["general_renderer", "parser"] } +/// ``` +/// +/// ## Step2 - Add Dependencies +/// Add `serde` dependency to `Cargo.toml` for serialization support +/// ```toml +/// [dependencies] +/// serde = { version = "1", features = ["derive"] } +/// ``` +/// +/// ## Step3 - Write Code +/// Write the following content into `main.rs` +/// +/// ## Step3 - Build and Run +/// ```bash +/// cargo run --manifest-path ./examples/example-general-renderer/Cargo.toml -- render Bob 22 +/// cargo run --manifest-path ./examples/example-general-renderer/Cargo.toml -- render Bob 22 --json +/// cargo run --manifest-path ./examples/example-general-renderer/Cargo.toml -- render Bob 22 --yaml +/// ``` +/// +/// Will print: +/// ```plain +/// Bob is 22 years old +/// {"member_name":"Bob","member_age":22} +/// member_name: Bob +/// member_age: 22 +/// ``` +/// +/// Cargo.toml +/// ``` +/// [package] +/// name = "example-general-renderer" +/// version = "0.0.1" +/// edition = "2024" +/// +/// [dependencies] +/// mingling = { path = "../../mingling", features = [ +/// "parser", +/// "general_renderer", +/// ] } +/// serde = { version = "1", features = ["derive"] } +/// tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] } +/// ``` +/// +/// main.rs +/// ```rust +/// use mingling::{ +/// AnyOutput, Groupped, +/// macros::{chain, dispatcher, gen_program, r_println, renderer}, +/// marker::NextProcess, +/// parser::Picker, +/// setup::GeneralRendererSetup, +/// }; +/// use serde::Serialize; +/// +/// dispatcher!("render", RenderCommand => RenderCommandEntry); +/// +/// #[tokio::main] +/// async fn main() { +/// let mut program = ThisProgram::new(); +/// // Add `GeneralRendererSetup` to receive user input `--json` `--yaml` parameters +/// program.with_setup(GeneralRendererSetup); +/// program.with_dispatcher(RenderCommand); +/// program.exec().await; +/// } +/// +/// // Manually implement Info struct +/// #[derive(Serialize, Groupped)] +/// struct Info { +/// #[serde(rename = "member_name")] +/// name: String, +/// #[serde(rename = "member_age")] +/// age: i32, +/// } +/// +/// #[chain] +/// async fn parse_render(prev: RenderCommandEntry) -> NextProcess { +/// let (name, age) = Picker::<AnyOutput<ThisProgram>>::new(prev.inner) +/// .pick::<String>(()) +/// .pick::<i32>(()) +/// .unpack_directly(); +/// AnyOutput::new(Info { name, age }).route_renderer() +/// } +/// +/// // Implement default renderer for when general_renderer is not specified +/// #[renderer] +/// fn render_info(prev: Info) { +/// r_println!("{} is {} years old", prev.name, prev.age); +/// } +/// +/// gen_program!(); +/// ``` +pub mod example_general_renderer {} +/// `Mingling` Example - Picker +/// +/// ## Step1 - Enable Feature +/// Enable the `parser` feature for mingling in `Cargo.toml` +/// ```toml +/// [dependencies] +/// mingling = { version = "...", features = ["parser"] } +/// ``` +/// +/// ## Step2 - Write Code +/// Write the following content into `main.rs` +/// +/// ## Step3 - Build and Run +/// ```bash +/// cargo run --manifest-path ./examples/example-picker/Cargo.toml -- pick Bob +/// cargo run --manifest-path ./examples/example-picker/Cargo.toml -- pick Bob --age -15 +/// cargo run --manifest-path ./examples/example-picker/Cargo.toml -- pick --age 99 +/// ``` +/// +/// Cargo.toml +/// ``` +/// [package] +/// name = "example-picker" +/// version = "0.0.1" +/// edition = "2024" +/// +/// [dependencies] +/// mingling = { path = "../../mingling", features = ["parser"] } +/// tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] } +/// ``` +/// +/// main.rs +/// ```rust +/// use mingling::{ +/// AnyOutput, +/// macros::{chain, dispatcher, gen_program, pack, r_println, renderer}, +/// marker::NextProcess, +/// parser::Picker, +/// }; +/// +/// dispatcher!("pick", PickCommand => PickEntry); +/// +/// #[tokio::main] +/// async fn main() { +/// let mut program = ThisProgram::new(); +/// program.with_dispatcher(PickCommand); +/// program.exec().await; +/// } +/// +/// pack!(NoNameProvided = ()); +/// pack!(ParsedPickInput = (i32, String)); +/// +/// #[chain] +/// async fn parse(prev: PickEntry) -> NextProcess { +/// // Extract arguments from `PickEntry`'s inner and create a `Picker` +/// let picker = Picker::new(prev.inner); +/// let picked = picker +/// // First extract the named argument +/// .pick_or("--age", 20) +/// .after(|n: i32| n.clamp(0, 100)) +/// // Then sequentially extract the remaining arguments +/// .pick_or_route((), AnyOutput::new(NoNameProvided::default())) +/// .unpack(); +/// +/// match picked { +/// Ok(value) => ParsedPickInput::new(value).to_render(), +/// Err(e) => e.route_renderer(), +/// } +/// } +/// +/// #[renderer] +/// fn render_parsed_pick_input(prev: ParsedPickInput) { +/// let (age, name) = prev.inner; +/// r_println!("Picked: name = {}, age = {}", name, age); +/// } +/// +/// #[renderer] +/// fn render_no_name_input(_prev: NoNameProvided) { +/// r_println!("No name provided."); +/// } +/// +/// gen_program!(); +/// ``` +pub mod example_picker {}
\ No newline at end of file diff --git a/mingling/src/example_docs.rs.tmpl b/mingling/src/example_docs.rs.tmpl new file mode 100644 index 0000000..5a32126 --- /dev/null +++ b/mingling/src/example_docs.rs.tmpl @@ -0,0 +1,18 @@ +// Auto generated + +>>>>>>>>>> examples + +@@@ >>> examples +<<<example_header>>> +/// +/// Cargo.toml +/// ``` +<<<example_import>>> +/// ``` +/// +/// main.rs +/// ```rust +<<<example_code>>> +/// ``` +pub mod <<<example_name>>> {} +@@@ <<< diff --git a/mingling/src/lib.rs b/mingling/src/lib.rs index 8d0a8c2..2bd6b58 100644 --- a/mingling/src/lib.rs +++ b/mingling/src/lib.rs @@ -101,163 +101,9 @@ pub mod macros { /// derive macro Groupped pub use mingling_macros::Groupped; -pub mod docs { - pub mod basic { - //! # Basic Usage - //! - //! This module demonstrates basic usage of Mingling with a simple "hello" command. - //! - //! ## Example - //! - //! ```rust - //! use mingling::{ - //! macros::{chain, dispatcher, gen_program, pack, r_println, renderer}, - //! marker::NextProcess, - //! }; - //! - //! // Define dispatcher `HelloCommand`, directing subcommand "hello" to `HelloEntry` - //! dispatcher!("hello", HelloCommand => HelloEntry); - //! - //! #[tokio::main] - //! async fn main() { - //! // Create program - //! let mut program = ThisProgram::new(); - //! - //! // Add dispatcher `HelloCommand` - //! program.with_dispatcher(HelloCommand); - //! - //! // Run program - //! program.exec().await; - //! } - //! - //! // Register wrapper type `Hello`, setting inner to `String` - //! pack!(Hello = String); - //! - //! // Register chain to `ThisProgram`, handling logic from `HelloEntry` - //! #[chain] - //! async fn parse_name(prev: HelloEntry) -> NextProcess { - //! // Extract string from `HelloEntry` as argument - //! let name = prev.get(0).cloned().unwrap_or_else(|| "World".to_string()); - //! - //! // Build `Hello` type and route to renderer - //! Hello::new(name).to_render() - //! } - //! - //! // Register renderer to `ThisProgram`, handling rendering of `Hello` - //! #[renderer] - //! fn render_hello_who(prev: Hello) { - //! // Print message - //! r_println!("Hello, {}!", *prev); - //! - //! // Program ends here - //! } - //! - //! // Generate program, default is `ThisProgram` - //! gen_program!(); - //! ``` - //! - //! ## Output - //! - //! ```text - //! > mycmd hello - //! Hello, World! - //! > mycmd hello Alice - //! Hello, Alice! - //! ``` - } +mod example_docs; - pub mod parser { - //! # Feature `parser` Usage - //! - //! This module demonstrates advanced usage of Mingling with the `Picker` utility for argument parsing. - //! - //! ## Example - //! - //! ```rust - //! use mingling::{ - //! AnyOutput, - //! macros::{chain, dispatcher, gen_program, pack, r_println, renderer}, - //! marker::NextProcess, - //! parser::Picker, - //! }; - //! - //! // Define dispatcher `RepeatCommand`, directing subcommand "repeat" to `RepeatEntry` - //! dispatcher!("repeat", RepeatCommand => RepeatEntry); - //! - //! #[tokio::main] - //! async fn main() { - //! // Create program - //! let mut program = ThisProgram::new(); - //! - //! // Add dispatcher `RepeatCommand` - //! program.with_dispatcher(RepeatCommand); - //! - //! // Run program - //! program.exec().await; - //! } - //! - //! // Register wrapper type `RepeatArgument`, setting inner to `(i32, String)` - //! pack!(RepeatArgument = (i32, String)); - //! - //! // Register error type - //! pack!(ErrorContentRequired = ()); - //! - //! // Register chain to `ThisProgram`, handling logic for `RepeatEntry` - //! #[chain] - //! async fn parse_repeat_args(prev: RepeatEntry) -> NextProcess { - //! let picker = Picker::new(prev.inner); // Create Picker from user arguments - //! let picked = picker - //! .pick_or::<i32>("--time", 1) // Extract argument `--time` - //! .after(|n| n.clamp(1, 20)) // Clamp extracted number between 1 - 20 - //! // Extract first remaining argument as content, route to type `ErrorContentRequired` if not found - //! .pick_or_route((), AnyOutput::new(ErrorContentRequired::default())) - //! .unpack(); // Unpack - //! - //! match picked { - //! Ok(args) => { - //! // Build `RepeatArgument` type and route to renderer - //! RepeatArgument::new(args).to_render() - //! } - //! Err(e) => { - //! // Extraction failed, route to error type - //! e.route_renderer() - //! } - //! } - //! } - //! - //! // Render `RepeatArgument` - //! #[renderer] - //! fn render_repeat(prev: RepeatArgument) { - //! let (times, content) = prev.inner; - //! for _ in 0..times { - //! r_println!("{}", content); - //! } - //! } - //! - //! // Render `ErrorContentRequired` - //! #[renderer] - //! fn render_error_content_required(_prev: ErrorContentRequired) { - //! r_println!("Error: content is required"); - //! } - //! - //! // Generate program, default is `ThisProgram` - //! gen_program!(); - //! ``` - //! - //! ## Output - //! - //! ```text - //! > mycmd repeat --time 3 Hello - //! Hello - //! Hello - //! Hello - //! > mycmd repeat --time 25 Hello - //! Hello - //! Hello - //! Hello - //! ... (repeated 20 times, clamped from 25) - //! > mycmd repeat --time 3 - //! Error: content is required - //! ``` - } +/// Example projects for `Mingling`, for learning how to use `Mingling` +pub mod _mingling_examples { + pub use crate::example_docs::*; } |
