// Auto generated /// `Mingling` Example - Async /// /// After enabling the `async` feature: /// 1. The `chain!` macro will support using **async** functions, /// 2. The `exec` function of `Program` will return a `Future` for you to use with an async runtime /// /// ## Enable Feature /// Enable the `async` feature for mingling in `Cargo.toml` /// ```toml /// [dependencies] /// mingling = { version = "...", features = ["async"] } /// ``` /// /// # How to Run /// ```bash /// cargo run --manifest-path ./examples/example-async/Cargo.toml -- hello World /// ``` /// /// Cargo.toml /// ```ignore /// [package] /// name = "example-async" /// version = "0.0.1" /// edition = "2024" /// /// [dependencies] /// tokio = { version = "1", features = ["full"] } /// mingling = { path = "../../mingling", features = ["async"] } /// ``` /// /// main.rs /// ```ignore /// use mingling::prelude::*; /// /// dispatcher!("hello", HelloCommand => HelloEntry); /// /// // Use Tokio async runtime /// #[tokio::main] /// async fn main() { /// let mut program = ThisProgram::new(); /// program.with_dispatcher(HelloCommand); /// /// // Run program /// program.exec().await; /// } /// /// pack!(Hello = String); /// /// // You can freely use async / non-async functions to declare your Chain /// /// #[chain] /// // fn parse_name(prev: HelloEntry) -> Next { /// async fn parse_name(prev: HelloEntry) -> Next { /// let name = prev.first().cloned().unwrap_or_else(|| "World".to_string()); /// Hello::new(name).to_render() /// } /// /// // For renderers, you can still only use synchronous functions /// #[renderer] /// fn render_hello_who(prev: Hello) { /// r_println!("Hello, {}!", *prev); /// } /// /// gen_program!(); /// ``` pub mod example_async {} /// `Mingling` Example - Basic /// /// # How to Run /// ```bash /// cargo run --manifest-path ./examples/example-basic/Cargo.toml -- hello World /// ``` /// /// Cargo.toml /// ```ignore /// [package] /// name = "example-basic" /// version = "0.0.1" /// edition = "2024" /// /// [dependencies] /// mingling = { path = "../../mingling" } /// ``` /// /// main.rs /// ```ignore /// use mingling::prelude::*; /// /// // Define dispatcher `HelloCommand`, directing subcommand "hello" to `HelloEntry` /// dispatcher!("hello", HelloCommand => HelloEntry); /// /// fn main() { /// // Create program /// let mut program = ThisProgram::new(); /// /// // Add dispatcher `HelloCommand` /// program.with_dispatcher(HelloCommand); /// /// // Run program /// program.exec(); /// } /// /// // Register wrapper type `Hello`, setting inner to `String` /// pack!(Hello = String); /// /// // Register chain to `ThisProgram`, handling logic from `HelloEntry` /// #[chain] /// fn parse_name(prev: HelloEntry) -> Next { /// // 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 /// [dependencies] /// mingling = { version = "...", features = [ /// "comp", // Enable this feature /// "parser" /// ] } /// ``` /// /// 2. Add `mingling` as a build dependency, enabling the `builds` and `comp` features /// ```toml /// [build-dependencies] /// mingling = { version = "...", features = [ /// "builds", // Enable this feature for build scripts /// "comp" /// ] } /// ``` /// /// 3. Write `build.rs` to generate completion scripts at compile time /// ```ignore /// use mingling::build::{build_comp_scripts, build_comp_scripts_with_bin_name}; /// fn main() { /// // Generate completion scripts for the current program, using the Cargo package name as the binary filename /// build_comp_scripts(env!("CARGO_PKG_NAME")).unwrap(); /// /// // Or, explicitly specify the binary filename /// // build_comp_scripts("your_bin").unwrap(); /// } /// ``` /// /// 4. Write `main.rs`, adding completion logic for your command entry point /// 5. Execute `cargo install --path ./`, then run the corresponding completion script in your shell /// /// Cargo.toml /// ```ignore /// [package] /// name = "example-completion" /// version = "0.0.1" /// edition = "2024" /// /// [dependencies] /// mingling = { path = "../../mingling", features = ["comp", "parser"] } /// ``` /// /// main.rs /// ```ignore /// use mingling::prelude::*; /// use mingling::{ /// macros::{suggest, suggest_enum}, /// parser::{PickableEnum, Picker}, /// EnumTag, Groupped, ShellContext, Suggest, /// }; /// /// // Define dispatcher `FruitCommand`, directing subcommand "fruit" to `FruitEntry` /// dispatcher!("fruit", FruitCommand => FruitEntry); /// /// #[completion(FruitEntry)] /// fn comp_fruit_command(ctx: &ShellContext) -> Suggest { /// if ctx.filling_argument_first("--name") { /// return suggest!(); /// } /// if ctx.filling_argument_first("--type") { /// return suggest_enum!(FruitType); /// } /// if ctx.typing_argument() { /// return suggest! { /// "--name": "Fruit name", /// "--type": "Fruit type" /// } /// .strip_typed_argument(ctx); /// } /// return suggest!(); /// } /// /// fn main() { /// let mut program = ThisProgram::new(); /// program.with_dispatcher(CompletionDispatcher); /// program.with_dispatcher(FruitCommand); /// program.exec(); /// } /// /// #[derive(Groupped)] /// struct FruitInfo { /// name: String, /// fruit_type: FruitType, /// } /// /// #[derive(Default, Debug, EnumTag)] /// enum FruitType { /// #[enum_desc("It's Apple")] /// #[enum_rename("apple")] /// FruitApple, /// /// #[enum_desc("It's Banana")] /// #[enum_rename("banana")] /// FruitBanana, /// /// #[enum_desc("It's Cherry")] /// #[enum_rename("cherry")] /// FruitCherry, /// /// #[enum_desc("It's Date")] /// #[enum_rename("date")] /// FruitDate, /// /// #[enum_desc("It's Elderberry")] /// #[enum_rename("elderberry")] /// FruitElderberry, /// /// #[default] /// #[enum_rename("unknown")] /// Unknown, /// } /// /// impl PickableEnum for FruitType {} /// /// #[chain] /// fn parse_fruit_info(prev: FruitEntry) -> Next { /// let picker = Picker::from(prev.inner); /// let (fruit_name, fruit_type) = picker.pick("--name").pick("--type").unpack(); /// let info = FruitInfo { /// name: fruit_name, /// fruit_type, /// }; /// info.to_render() /// } /// /// #[renderer] /// fn render_fruit(prev: FruitInfo) { /// match (prev.name.is_empty(), prev.fruit_type) { /// (true, FruitType::Unknown) => { /// r_println!("Fruit name is empty and type is unknown"); /// } /// (true, fruit_type) => { /// r_println!("Fruit name is empty, Type: {:?}", fruit_type); /// } /// (false, FruitType::Unknown) => { /// r_println!("Fruit name: {}, Type is unknown", prev.name); /// } /// (false, fruit_type) => { /// r_println!("Fruit name: {}, Type: {:?}", prev.name, fruit_type); /// } /// } /// } /// /// gen_program!(); /// ``` pub mod example_completion {} /// `Mingling` Example - Dispatch Tree /// /// # How to Deploy /// 1. Enable the `dispatch_tree` feature (`comp` is optional) /// ```toml /// mingling = { version = "...", features = [ /// "dispatch_tree", // Enable this feature /// "comp" // optional /// ] } /// ``` /// /// 2. Using `cargo expand`: /// /// ```bash /// cargo expand --manifest-path examples/example-dispatch-tree/Cargo.toml > expanded.rs /// cat expanded.rs /// ``` /// /// Cargo.toml /// ```ignore /// [package] /// name = "example-dispatch-tree" /// version = "0.1.0" /// edition = "2024" /// /// [dependencies] /// mingling = { path = "../../mingling", features = ["dispatch_tree", "comp"] } /// ``` /// /// main.rs /// ```ignore /// #![allow(unused_mut)] /// /// use mingling::prelude::*; /// /// fn main() { /// let mut program = ThisProgram::new(); /// /// // // After enabling `dispatch_tree`, this method will no longer exist /// // program.with_dispatcher(CommandGreet); /// // /// // // The `CompletionDispatcher` automatically generated by `comp` will also be imported automatically /// // program.with_dispatcher(CompletionDispatcher); /// /// program.exec(); /// } /// /// dispatcher!("greet", CommandGreet => EntryGreet); /// dispatcher!("help", CommandHelp => EntryHelp); /// dispatcher!("quit", CommandQuit => EntryQuit); /// dispatcher!("list", CommandList => EntryList); /// dispatcher!("status", CommandStatus => EntryStatus); /// dispatcher!("save", CommandSave => EntrySave); /// dispatcher!("load", CommandLoad => EntryLoad); /// dispatcher!("config", CommandConfig => EntryConfig); /// dispatcher!("run", CommandRun => EntryRun); /// dispatcher!("debug", CommandDebug => EntryDebug); /// dispatcher!("version", CommandVersion => EntryVersion); /// /// gen_program!(); /// ``` pub mod example_dispatch_tree {} /// `Mingling` Example - Exit Code /// /// This example demonstrates how to modify the program's exit code using `ExitCodeSetup`. /// By default, the program exits with code 0. This example shows: /// 1. Using `dispatcher!` to define an error command, /// 2. Using `chain!` to handle errors and set a custom exit code via `ProgramExitCode`, /// 3. Using `renderer!` to print an error message. /// /// # How to Run /// ```bash /// cargo run --manifest-path ./examples/example-exit-code/Cargo.toml -- error /// ``` /// /// Cargo.toml /// ```ignore /// [package] /// name = "example-exit-code" /// version = "0.1.0" /// edition = "2024" /// /// [dependencies] /// mingling = { path = "../../mingling" } /// ``` /// /// main.rs /// ```ignore /// use mingling::prelude::*; /// use mingling::{ /// res::{ExitCode, exit_code}, /// setup::ExitCodeSetup, /// }; /// /// fn main() { /// let mut program = ThisProgram::new(); /// program.with_dispatcher(ErrorCommand); /// program.with_setup(ExitCodeSetup::::default()); /// program.exec_and_exit(); /// } /// /// dispatcher!("error", ErrorCommand => ErrorEntry); /// pack!(ResultError = ()); /// /// #[chain] /// fn handle_error_entry(_prev: ErrorEntry, ec: &mut ExitCode) -> Next { /// ec.exit_code = 1; /// return ResultError::default(); /// } /// /// #[renderer] /// fn render_error(_prev: ResultError) { /// let exit_code = exit_code::(); /// r_println!("Exit with exit code: {}", exit_code); /// } /// /// gen_program!(); /// ``` pub mod example_exit_code {} /// `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` /// /// ## Step4 - 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 /// ```ignore /// [package] /// name = "example-general-renderer" /// version = "0.0.1" /// edition = "2024" /// /// [dependencies] /// mingling = { path = "../../mingling", features = [ /// "parser", /// "general_renderer", /// "json_serde_fmt", /// "yaml_serde_fmt", /// ] } /// serde = { version = "1", features = ["derive"] } /// ``` /// /// main.rs /// ```ignore /// use mingling::prelude::*; /// use mingling::{parser::Picker, setup::GeneralRendererSetup, Groupped}; /// use serde::Serialize; /// /// dispatcher!("render", RenderCommand => RenderCommandEntry); /// /// 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(); /// } /// /// // Manually implement Info struct /// #[derive(Serialize, Groupped)] /// struct Info { /// #[serde(rename = "member_name")] /// name: String, /// #[serde(rename = "member_age")] /// age: i32, /// } /// /// #[chain] /// fn parse_render(prev: RenderCommandEntry) -> Next { /// let (name, age) = Picker::new(prev.inner) /// .pick::(()) /// .pick::(()) /// .unpack(); /// Info { name, age }.to_render() /// } /// /// // 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 /// ```ignore /// [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 /// ```ignore /// use mingling::prelude::*; /// /// dispatcher!("pick", PickCommand => PickEntry); /// /// fn main() { /// let mut program = ThisProgram::new(); /// program.with_dispatcher(PickCommand); /// program.exec(); /// } /// /// pack!(NoNameProvided = ()); /// pack!(ParsedPickInput = (i32, String)); /// /// #[chain] /// fn parse(prev: PickEntry) -> Next { /// let picked = prev /// // 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((), NoNameProvided::default().to_render()) /// .unpack(); /// /// match picked { /// Ok(value) => ParsedPickInput::new(value).to_render(), /// Err(e) => e, /// } /// } /// /// #[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 {} /// /// Cargo.toml /// ```ignore /// [package] /// name = "example-repl" /// version = "0.0.1" /// edition = "2024" /// /// [dependencies] /// mingling = { path = "../../mingling", features = ["repl", "parser"] } /// just_fmt = "0.1.2" /// ``` /// /// main.rs /// ```ignore /// use mingling::{ /// hook::ProgramHook, /// prelude::*, /// setup::{BasicREPLOutputSetup, BasicREPLPromptSetup, BasicREPLReadlineSetup}, /// this, REPL, /// }; /// use std::{env::current_dir, path::PathBuf}; /// /// // Resource to store the current directory /// #[derive(Clone)] /// struct CurrentDir { /// dir: PathBuf, /// } /// /// impl Default for CurrentDir { /// fn default() -> Self { /// Self { /// dir: current_dir().unwrap(), /// } /// } /// } /// /// fn main() { /// let mut program = ThisProgram::new(); /// /// // Add resource /// program.with_resource(CurrentDir::default()); /// /// // Add dispatchers /// program.with_dispatcher(ChangeDirectoryCommand); /// program.with_dispatcher(ListCommand); /// program.with_dispatcher(ExitCommand); /// program.with_dispatcher(ClearCommand); /// /// // Add setups /// program.with_setup(BasicREPLReadlineSetup); /// program.with_setup(BasicREPLOutputSetup); /// program.with_setup(BasicREPLPromptSetup::func(|| { /// let res = this::().res::().unwrap(); /// let dir_str: String = res.dir.to_string_lossy().into(); /// let prompt = format!( /// "{}> ", /// dir_str /// .replace(&['/', '\\'][..], ">") /// .trim_start_matches('>') /// .trim_end_matches('>') /// ); /// prompt /// })); /// /// // Add hooks to handle REPL-related events /// program.with_hook(ProgramHook::empty().on_repl_begin(|| { /// // Print welcome message /// println!("Welcome!") /// })); /// /// // Start the REPL loop /// program.exec_repl(); /// } /// /// // Create error route /// pack!(ErrorDirectoryNotExist = PathBuf); /// /// // Create commands: cd ls exit /// dispatcher!("cd", ChangeDirectoryCommand => ChangeDirectoryEntry); /// dispatcher!("ls", ListCommand => ListEntry); /// dispatcher!("exit", ExitCommand => ExitEntry); /// dispatcher!("clear", ClearCommand => ClearEntry); /// /// // Define data needed for the cd command's execution phase /// pack!(StateChangeDirectory = String); /// /// // Define data needed for the ls command's rendering phase /// pack!(ResultList = Vec); /// /// // Parse cd command arguments /// #[chain] /// fn parse_cd_args(prev: ChangeDirectoryEntry) -> Next { /// let join = prev.pick(()).unpack(); /// StateChangeDirectory::new(join) /// } /// /// // Execute directory change /// #[chain] /// fn handle_cd(prev: StateChangeDirectory, current_dir: &mut CurrentDir) -> Next { /// let join = prev.inner; /// let new_dir = just_fmt::fmt_path::fmt_path(current_dir.dir.join(join)).unwrap_or_default(); /// /// // If the path is not found, route to error handling /// if !new_dir.exists() { /// return ErrorDirectoryNotExist::new(new_dir).to_render(); /// } /// /// current_dir.dir = new_dir; /// empty_result!() /// } /// /// // Get directory contents via the CurrentDir resource /// #[chain] /// fn handle_ls(_prev: ListEntry, current_dir: &CurrentDir) -> Next { /// let dir = ¤t_dir.dir; /// let entries: Vec = std::fs::read_dir(dir) /// .into_iter() /// .flat_map(|rd| rd.filter_map(|e| e.ok())) /// .map(|e| { /// let name = e.file_name().to_string_lossy().to_string(); /// if e.file_type().map(|t| t.is_dir()).unwrap_or(false) { /// format!("{}/", name) /// } else { /// name /// } /// }) /// .collect(); /// /// // Render ResultList /// ResultList::new(entries).to_render() /// } /// /// // Render ResultList data /// #[renderer] /// fn render_list(list: ResultList) { /// for item in list.inner { /// r_println!("{}", item) /// } /// } /// /// // Handle exit command event /// #[chain] /// fn handle_exit( /// _prev: ExitEntry, /// repl: &mut REPL, // Import REPL resource, registered in `exec_repl`, usable directly /// ) { /// // Set the REPL exit flag; REPL will exit after this loop iteration /// repl.exit = true; /// } /// /// // Handle clear command event /// #[chain] /// fn handle_clear(_prev: ClearEntry) { /// // Clear the terminal screen /// print!("\x1B[2J\x1B[1;1H"); /// } /// /// // Handle path not found event /// #[renderer] /// fn render_error_directory_not_exist(err: ErrorDirectoryNotExist) { /// r_println!("Directory not found: {}", err.inner.display()) /// } /// /// // Handle dispatcher not found event /// #[renderer] /// fn dispatcher_not_found(prev: DispatcherNotFound) { /// r_println!("Command not found: \"{}\"", prev.join(", ")) /// } /// /// gen_program!(); /// ``` pub mod example_repl {} /// `Mingling` Example - Global Resource Injection /// /// This example demonstrates how to use global resource injection in `#[chain]` functions. /// You can inject both immutable (`&T`) and mutable (`&mut T`) references to global resources. /// /// # How to Run /// ```bash /// cargo run --manifest-path ./examples/example-resources/Cargo.toml -- setup /// ``` /// /// Cargo.toml /// ```ignore /// [package] /// name = "example-resources" /// version = "0.0.1" /// edition = "2024" /// /// [dependencies] /// mingling = { path = "../../mingling", features = ["parser"] } /// ``` /// /// main.rs /// ```ignore /// use mingling::prelude::*; /// use std::{env::current_dir, path::PathBuf}; /// /// // Define a resource for storing global state /// #[derive(Default, Clone)] /// pub struct MyResource { /// current_dir: PathBuf, /// } /// /// fn main() { /// let mut program = ThisProgram::new(); /// /// // Add the resource to the program /// program.with_resource(MyResource::default()); /// /// program.with_dispatcher(SetupCommand); /// program.exec_and_exit(); /// } /// /// dispatcher!("setup", SetupCommand => SetupEntry); /// pack!(StateRead = ()); /// pack!(ResultCurrentDir = PathBuf); /// /// #[chain] /// fn setup( /// _prev: SetupEntry, /// resource: &mut MyResource, // Import the resource into `setup` /// ) -> Next { /// // Set the global resource /// resource.current_dir = current_dir().unwrap(); /// /// StateRead::default() /// } /// /// #[chain] /// fn read(_prev: StateRead, resource: &MyResource) -> Next { /// // Read the global resource /// let current_dir = resource.current_dir.clone(); /// ResultCurrentDir::new(current_dir).to_render() /// } /// /// #[renderer] /// fn render_current_dir(dir: ResultCurrentDir) { /// r_println!("Current dir: {}", dir.to_string_lossy()) /// } /// /// gen_program!(); /// ``` pub mod example_resources {}