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 {}