Skip to main content

mingling/
example_docs.rs

1// Auto generated
2
3/// `Mingling` Example - Async
4///
5///  After enabling the `async` feature:
6///  1. The `chain!` macro will support using **async** functions,
7///  2. The `exec` function of `Program` will return a `Future` for you to use with an async runtime
8///
9///  ## Enable Feature
10///  Enable the `async` feature for mingling in `Cargo.toml`
11///  ```toml
12///  [dependencies]
13///  mingling = { version = "...", features = ["async"] }
14///  ```
15///
16///  # How to Run
17///  ```bash
18///  cargo run --manifest-path ./examples/example-async/Cargo.toml -- hello World
19///  ```
20///
21/// Cargo.toml
22/// ```ignore
23/// [package]
24/// name = "example-async"
25/// version = "0.0.1"
26/// edition = "2024"
27///
28/// [dependencies]
29/// tokio = { version = "1", features = ["full"] }
30/// mingling = { path = "../../mingling", features = ["async"] }
31/// ```
32///
33/// main.rs
34/// ```ignore
35/// use mingling::prelude::*;
36///
37/// dispatcher!("hello", HelloCommand => HelloEntry);
38///
39/// // Use Tokio async runtime
40/// #[tokio::main]
41/// async fn main() {
42///     let mut program = ThisProgram::new();
43///     program.with_dispatcher(HelloCommand);
44///
45///     // Run program
46///     program.exec().await;
47/// }
48///
49/// pack!(Hello = String);
50///
51/// // You can freely use async / non-async functions to declare your Chain
52///
53/// #[chain]
54/// // fn parse_name(prev: HelloEntry) -> Next {
55/// async fn parse_name(prev: HelloEntry) -> Next {
56///     let name = prev.first().cloned().unwrap_or_else(|| "World".to_string());
57///     Hello::new(name).to_render()
58/// }
59///
60/// // For renderers, you can still only use synchronous functions
61/// #[renderer]
62/// fn render_hello_who(prev: Hello) {
63///     r_println!("Hello, {}!", *prev);
64/// }
65///
66/// gen_program!();
67/// ```
68pub mod example_async {}
69/// `Mingling` Example - Basic
70///
71///  # How to Run
72///  ```bash
73///  cargo run --manifest-path ./examples/example-basic/Cargo.toml -- hello World
74///  ```
75///
76/// Cargo.toml
77/// ```ignore
78/// [package]
79/// name = "example-basic"
80/// version = "0.0.1"
81/// edition = "2024"
82///
83/// [dependencies]
84/// mingling = { path = "../../mingling" }
85/// ```
86///
87/// main.rs
88/// ```ignore
89/// use mingling::prelude::*;
90///
91/// // Define dispatcher `HelloCommand`, directing subcommand "hello" to `HelloEntry`
92/// dispatcher!("hello", HelloCommand => HelloEntry);
93///
94/// fn main() {
95///     // Create program
96///     let mut program = ThisProgram::new();
97///
98///     // Add dispatcher `HelloCommand`
99///     program.with_dispatcher(HelloCommand);
100///
101///     // Run program
102///     program.exec();
103/// }
104///
105/// // Register wrapper type `Hello`, setting inner to `String`
106/// pack!(Hello = String);
107///
108/// // Register chain to `ThisProgram`, handling logic from `HelloEntry`
109/// #[chain]
110/// fn parse_name(prev: HelloEntry) -> Next {
111///     // Extract string from `HelloEntry` as argument
112///     let name = prev.first().cloned().unwrap_or_else(|| "World".to_string());
113///
114///     // Build `Hello` type and route to renderer
115///     Hello::new(name).to_render()
116/// }
117///
118/// // Register renderer to `ThisProgram`, handling rendering of `Hello`
119/// #[renderer]
120/// fn render_hello_who(prev: Hello) {
121///     // Print message
122///     r_println!("Hello, {}!", *prev);
123///
124///     // Program ends here
125/// }
126///
127/// // Generate program, default is `ThisProgram`
128/// gen_program!();
129/// ```
130pub mod example_basic {}
131/// `Mingling` Example - Completion
132///
133///  # How to Deploy
134///  1. Enable the `comp` feature
135///  ```toml
136///  [dependencies]
137///  mingling = { version = "...", features = [
138///      "comp",  // Enable this feature
139///      "parser"
140///  ] }
141///  ```
142///
143///  2. Add `mingling` as a build dependency, enabling the `builds` and `comp` features
144///  ```toml
145///  [build-dependencies]
146///  mingling = { version = "...", features = [
147///      "builds", // Enable this feature for build scripts
148///      "comp"
149///  ] }
150///  ```
151///
152///  3. Write `build.rs` to generate completion scripts at compile time
153///  ```ignore
154///  use mingling::build::{build_comp_scripts, build_comp_scripts_with_bin_name};
155///  fn main() {
156///      // Generate completion scripts for the current program, using the Cargo package name as the binary filename
157///      build_comp_scripts(env!("CARGO_PKG_NAME")).unwrap();
158///
159///      // Or, explicitly specify the binary filename
160///      // build_comp_scripts("your_bin").unwrap();
161///  }
162///  ```
163///
164///  4. Write `main.rs`, adding completion logic for your command entry point
165///  5. Execute `cargo install --path ./`, then run the corresponding completion script in your shell
166///
167/// Cargo.toml
168/// ```ignore
169/// [package]
170/// name = "example-completion"
171/// version = "0.0.1"
172/// edition = "2024"
173///
174/// [dependencies]
175/// mingling = { path = "../../mingling", features = ["comp", "parser"] }
176/// ```
177///
178/// main.rs
179/// ```ignore
180/// use mingling::prelude::*;
181/// use mingling::{
182///     macros::{suggest, suggest_enum},
183///     parser::{PickableEnum, Picker},
184///     EnumTag, Groupped, ShellContext, Suggest,
185/// };
186///
187/// // Define dispatcher `FruitCommand`, directing subcommand "fruit" to `FruitEntry`
188/// dispatcher!("fruit", FruitCommand => FruitEntry);
189///
190/// #[completion(FruitEntry)]
191/// fn comp_fruit_command(ctx: &ShellContext) -> Suggest {
192///     if ctx.filling_argument_first("--name") {
193///         return suggest!();
194///     }
195///     if ctx.filling_argument_first("--type") {
196///         return suggest_enum!(FruitType);
197///     }
198///     if ctx.typing_argument() {
199///         return suggest! {
200///             "--name": "Fruit name",
201///             "--type": "Fruit type"
202///         }
203///         .strip_typed_argument(ctx);
204///     }
205///     return suggest!();
206/// }
207///
208/// fn main() {
209///     let mut program = ThisProgram::new();
210///     program.with_dispatcher(CompletionDispatcher);
211///     program.with_dispatcher(FruitCommand);
212///     program.exec();
213/// }
214///
215/// #[derive(Groupped)]
216/// struct FruitInfo {
217///     name: String,
218///     fruit_type: FruitType,
219/// }
220///
221/// #[derive(Default, Debug, EnumTag)]
222/// enum FruitType {
223///     #[enum_desc("It's Apple")]
224///     #[enum_rename("apple")]
225///     FruitApple,
226///
227///     #[enum_desc("It's Banana")]
228///     #[enum_rename("banana")]
229///     FruitBanana,
230///
231///     #[enum_desc("It's Cherry")]
232///     #[enum_rename("cherry")]
233///     FruitCherry,
234///
235///     #[enum_desc("It's Date")]
236///     #[enum_rename("date")]
237///     FruitDate,
238///
239///     #[enum_desc("It's Elderberry")]
240///     #[enum_rename("elderberry")]
241///     FruitElderberry,
242///
243///     #[default]
244///     #[enum_rename("unknown")]
245///     Unknown,
246/// }
247///
248/// impl PickableEnum for FruitType {}
249///
250/// #[chain]
251/// fn parse_fruit_info(prev: FruitEntry) -> Next {
252///     let picker = Picker::from(prev.inner);
253///     let (fruit_name, fruit_type) = picker.pick("--name").pick("--type").unpack();
254///     let info = FruitInfo {
255///         name: fruit_name,
256///         fruit_type,
257///     };
258///     info.to_render()
259/// }
260///
261/// #[renderer]
262/// fn render_fruit(prev: FruitInfo) {
263///     match (prev.name.is_empty(), prev.fruit_type) {
264///         (true, FruitType::Unknown) => {
265///             r_println!("Fruit name is empty and type is unknown");
266///         }
267///         (true, fruit_type) => {
268///             r_println!("Fruit name is empty, Type: {:?}", fruit_type);
269///         }
270///         (false, FruitType::Unknown) => {
271///             r_println!("Fruit name: {}, Type is unknown", prev.name);
272///         }
273///         (false, fruit_type) => {
274///             r_println!("Fruit name: {}, Type: {:?}", prev.name, fruit_type);
275///         }
276///     }
277/// }
278///
279/// gen_program!();
280/// ```
281pub mod example_completion {}
282/// `Mingling` Example - Dispatch Tree
283///
284///  # How to Deploy
285///  1. Enable the `dispatch_tree` feature (`comp` is optional)
286///  ```toml
287///  mingling = { version = "...", features = [
288///      "dispatch_tree",  // Enable this feature
289///      "comp" // optional
290///  ] }
291///  ```
292///
293///  2. Using `cargo expand`:
294///
295///  ```bash
296///  cargo expand --manifest-path examples/example-dispatch-tree/Cargo.toml > expanded.rs
297///  cat expanded.rs | grep dispatch_args_trie -A 264
298///  ```
299///
300/// Cargo.toml
301/// ```ignore
302/// [package]
303/// name = "example-dispatch-tree"
304/// version = "0.1.0"
305/// edition = "2024"
306///
307/// [dependencies]
308/// mingling = { path = "../../mingling", features = ["dispatch_tree", "comp"] }
309/// ```
310///
311/// main.rs
312/// ```ignore
313/// #![allow(unused_mut)]
314///
315/// use mingling::prelude::*;
316///
317/// fn main() {
318///     let mut program = ThisProgram::new();
319///
320///     // After enabling `dispatch_tree`, this method will no longer exist
321///     // program.with_dispatcher(CommandGreet);
322///     //
323///     // The `CompletionDispatcher` automatically generated by `comp` will also be imported
324///     // automatically
325///     // program.with_dispatcher(CompletionDispatcher);
326///
327///     program.exec();
328/// }
329///
330/// dispatcher!("greet", CommandGreet => EntryGreet);
331/// dispatcher!("help", CommandHelp => EntryHelp);
332/// dispatcher!("quit", CommandQuit => EntryQuit);
333/// dispatcher!("list", CommandList => EntryList);
334/// dispatcher!("status", CommandStatus => EntryStatus);
335/// dispatcher!("save", CommandSave => EntrySave);
336/// dispatcher!("load", CommandLoad => EntryLoad);
337/// dispatcher!("config", CommandConfig => EntryConfig);
338/// dispatcher!("run", CommandRun => EntryRun);
339/// dispatcher!("debug", CommandDebug => EntryDebug);
340/// dispatcher!("version", CommandVersion => EntryVersion);
341///
342/// gen_program!();
343/// ```
344pub mod example_dispatch_tree {}
345/// `Mingling` Example - Exit Code
346///
347///  This example demonstrates how to modify the program's exit code using `ExitCodeSetup`.
348///  By default, the program exits with code 0. This example shows:
349///  1. Using `dispatcher!` to define an error command,
350///  2. Using `chain!` to handle errors and set a custom exit code via `ProgramExitCode`,
351///  3. Using `renderer!` to print an error message.
352///
353///  # How to Run
354///  ```bash
355///  cargo run --manifest-path ./examples/example-exit-code/Cargo.toml -- error
356///  ```
357///
358/// Cargo.toml
359/// ```ignore
360/// [package]
361/// name = "example-exit-code"
362/// version = "0.1.0"
363/// edition = "2024"
364///
365/// [dependencies]
366/// mingling = { path = "../../mingling" }
367/// ```
368///
369/// main.rs
370/// ```ignore
371/// use mingling::prelude::*;
372/// use mingling::{
373///     res::{exit_code, update_exit_code},
374///     setup::ExitCodeSetup,
375/// };
376///
377/// fn main() {
378///     let mut program = ThisProgram::new();
379///     program.with_dispatcher(ErrorCommand);
380///     program.with_setup(ExitCodeSetup::<ThisProgram>::default());
381///     program.exec_and_exit();
382/// }
383///
384/// dispatcher!("error", ErrorCommand => ErrorEntry);
385/// pack!(ResultError = ());
386///
387/// #[chain]
388/// fn handle_error_entry(_prev: ErrorEntry) -> Next {
389///     update_exit_code::<ThisProgram>(1);
390///     return ResultError::default();
391/// }
392///
393/// #[renderer]
394/// fn render_error(_prev: ResultError) {
395///     let exit_code = exit_code::<ThisProgram>();
396///     r_println!("Exit with exit code: {}", exit_code);
397/// }
398///
399/// gen_program!();
400/// ```
401pub mod example_exit_code {}
402/// `Mingling` Example - General Renderer
403///
404///  ## Step1 - Enable Feature
405///  Enable the `general_renderer` feature for mingling in `Cargo.toml`
406///  ```toml
407///  [dependencies]
408///  mingling = { version = "...", features = ["general_renderer", "parser"] }
409///  ```
410///
411///  ## Step2 - Add Dependencies
412///  Add `serde` dependency to `Cargo.toml` for serialization support
413///  ```toml
414///  [dependencies]
415///  serde = { version = "1", features = ["derive"] }
416///  ```
417///
418///  ## Step3 - Write Code
419///  Write the following content into `main.rs`
420///
421///  ## Step4 - Build and Run
422///  ```bash
423///  cargo run --manifest-path ./examples/example-general-renderer/Cargo.toml -- render Bob 22
424///  cargo run --manifest-path ./examples/example-general-renderer/Cargo.toml -- render Bob 22 --json
425///  cargo run --manifest-path ./examples/example-general-renderer/Cargo.toml -- render Bob 22 --yaml
426///  ```
427///
428///  Will print:
429///  ```plain
430///  Bob is 22 years old
431///  {"member_name":"Bob","member_age":22}
432///  member_name: Bob
433///  member_age: 22
434///  ```
435///
436/// Cargo.toml
437/// ```ignore
438/// [package]
439/// name = "example-general-renderer"
440/// version = "0.0.1"
441/// edition = "2024"
442///
443/// [dependencies]
444/// mingling = { path = "../../mingling", features = [
445///     "parser",
446///     "general_renderer",
447///     "json_serde_fmt",
448///     "yaml_serde_fmt",
449/// ] }
450/// serde = { version = "1", features = ["derive"] }
451/// ```
452///
453/// main.rs
454/// ```ignore
455/// use mingling::prelude::*;
456/// use mingling::{parser::Picker, setup::GeneralRendererSetup, Groupped};
457/// use serde::Serialize;
458///
459/// dispatcher!("render", RenderCommand => RenderCommandEntry);
460///
461/// fn main() {
462///     let mut program = ThisProgram::new();
463///     // Add `GeneralRendererSetup` to receive user input `--json` `--yaml` parameters
464///     program.with_setup(GeneralRendererSetup);
465///     program.with_dispatcher(RenderCommand);
466///     program.exec();
467/// }
468///
469/// // Manually implement Info struct
470/// #[derive(Serialize, Groupped)]
471/// struct Info {
472///     #[serde(rename = "member_name")]
473///     name: String,
474///     #[serde(rename = "member_age")]
475///     age: i32,
476/// }
477///
478/// #[chain]
479/// fn parse_render(prev: RenderCommandEntry) -> Next {
480///     let (name, age) = Picker::new(prev.inner)
481///         .pick::<String>(())
482///         .pick::<i32>(())
483///         .unpack();
484///     Info { name, age }.to_render()
485/// }
486///
487/// // Implement default renderer for when general_renderer is not specified
488/// #[renderer]
489/// fn render_info(prev: Info) {
490///     r_println!("{} is {} years old", prev.name, prev.age);
491/// }
492///
493/// gen_program!();
494/// ```
495pub mod example_general_renderer {}
496/// `Mingling` Example - Picker
497///
498///  ## Step1 - Enable Feature
499///  Enable the `parser` feature for mingling in `Cargo.toml`
500///  ```toml
501///  [dependencies]
502///  mingling = { version = "...", features = ["parser"] }
503///  ```
504///
505///  ## Step2 - Write Code
506///  Write the following content into `main.rs`
507///
508///  ## Step3 - Build and Run
509///  ```bash
510///  cargo run --manifest-path ./examples/example-picker/Cargo.toml -- pick Bob
511///  cargo run --manifest-path ./examples/example-picker/Cargo.toml -- pick Bob --age -15
512///  cargo run --manifest-path ./examples/example-picker/Cargo.toml -- pick --age 99
513///  ```
514///
515/// Cargo.toml
516/// ```ignore
517/// [package]
518/// name = "example-picker"
519/// version = "0.0.1"
520/// edition = "2024"
521///
522/// [dependencies]
523/// mingling = { path = "../../mingling", features = ["parser"] }
524/// tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] }
525/// ```
526///
527/// main.rs
528/// ```ignore
529/// use mingling::prelude::*;
530///
531/// dispatcher!("pick", PickCommand => PickEntry);
532///
533/// fn main() {
534///     let mut program = ThisProgram::new();
535///     program.with_dispatcher(PickCommand);
536///     program.exec();
537/// }
538///
539/// pack!(NoNameProvided = ());
540/// pack!(ParsedPickInput = (i32, String));
541///
542/// #[chain]
543/// fn parse(prev: PickEntry) -> Next {
544///     let picked = prev
545///         // First extract the named argument
546///         .pick_or("--age", 20)
547///         .after(|n: i32| n.clamp(0, 100))
548///         // Then sequentially extract the remaining arguments
549///         .pick_or_route((), NoNameProvided::default().to_render())
550///         .unpack();
551///
552///     match picked {
553///         Ok(value) => ParsedPickInput::new(value).to_render(),
554///         Err(e) => e,
555///     }
556/// }
557///
558/// #[renderer]
559/// fn render_parsed_pick_input(prev: ParsedPickInput) {
560///     let (age, name) = prev.inner;
561///     r_println!("Picked: name = {}, age = {}", name, age);
562/// }
563///
564/// #[renderer]
565/// fn render_no_name_input(_prev: NoNameProvided) {
566///     r_println!("No name provided.");
567/// }
568///
569/// gen_program!();
570/// ```
571pub mod example_picker {}
572/// `Mingling` Example - Global Resource Injection
573///
574///  This example demonstrates how to use global resource injection in `#[chain]` functions.
575///  You can inject both immutable (`&T`) and mutable (`&mut T`) references to global resources.
576///
577///  # How to Run
578///  ```bash
579///  cargo run --manifest-path ./examples/example-resources/Cargo.toml -- setup
580///  ```
581///
582/// Cargo.toml
583/// ```ignore
584/// [package]
585/// name = "example-resources"
586/// version = "0.0.1"
587/// edition = "2024"
588///
589/// [dependencies]
590/// mingling = { path = "../../mingling", features = ["parser"] }
591/// ```
592///
593/// main.rs
594/// ```ignore
595/// use mingling::prelude::*;
596/// use std::{env::current_dir, path::PathBuf};
597///
598/// // Define a resource for storing global state
599/// #[derive(Default, Clone)]
600/// pub struct MyResource {
601///     current_dir: PathBuf,
602/// }
603///
604/// fn main() {
605///     let mut program = ThisProgram::new();
606///
607///     // Add the resource to the program
608///     program.with_resource(MyResource::default());
609///
610///     program.with_dispatcher(SetupCommand);
611///     program.exec_and_exit();
612/// }
613///
614/// dispatcher!("setup", SetupCommand => SetupEntry);
615/// pack!(StateRead = ());
616/// pack!(ResultCurrentDir = PathBuf);
617///
618/// #[chain]
619/// fn setup(
620///     _prev: SetupEntry,
621///     resource: &mut MyResource, // Import the resource into `setup`
622/// ) -> Next {
623///     // Set the global resource
624///     resource.current_dir = current_dir().unwrap();
625///
626///     StateRead::default()
627/// }
628///
629/// #[chain]
630/// fn read(_prev: StateRead, resource: &MyResource) -> Next {
631///     // Read the global resource
632///     let current_dir = resource.current_dir.clone();
633///     ResultCurrentDir::new(current_dir).to_render()
634/// }
635///
636/// #[renderer]
637/// fn render_current_dir(dir: ResultCurrentDir) {
638///     r_println!("Current dir: {}", dir.to_string_lossy())
639/// }
640///
641/// gen_program!();
642/// ```
643pub mod example_resources {}