builder_api_demo/
builder_api_demo.rs

1//! Demonstrates the improved builder API features
2use flag_rs::{CommandBuilder, Flag};
3
4fn main() {
5    // Demonstrates all the new builder API improvements
6    let app = CommandBuilder::new("app")
7        .short("Modern CLI app with improved API")
8        .long(
9            "This example showcases the builder API improvements including:\n\
10               - Type-specific flag constructors\n\
11               - Inline flag completions\n\
12               - Bulk flag/subcommand methods\n\
13               - Type-safe context access",
14        )
15        // Bulk flag addition
16        .flags(vec![
17            // Type-specific constructors
18            Flag::bool("verbose")
19                .short('v')
20                .usage("Enable verbose output")
21                .default_bool(false),
22            Flag::bool("quiet").short('q').usage("Suppress all output"),
23            Flag::int("threads")
24                .short('t')
25                .usage("Number of worker threads")
26                .default_int(4),
27            Flag::choice("log-level", &["debug", "info", "warn", "error"])
28                .usage("Set the logging level")
29                .default_str("info")
30                .completion(|_ctx, prefix| {
31                    // Inline completion with descriptions
32                    let levels = vec![
33                        ("debug", "Show all messages including debug"),
34                        ("info", "Show informational messages and above"),
35                        ("warn", "Show warnings and errors only"),
36                        ("error", "Show errors only"),
37                    ];
38
39                    let mut result = flag_rs::CompletionResult::new();
40                    for (level, desc) in levels {
41                        if level.starts_with(prefix) {
42                            result =
43                                result.add_with_description(level.to_string(), desc.to_string());
44                        }
45                    }
46                    Ok(result)
47                }),
48        ])
49        // Bulk subcommand addition
50        .subcommands(vec![
51            CommandBuilder::new("init")
52                .short("Initialize a new project")
53                .flags(vec![
54                    Flag::string("name")
55                        .short('n')
56                        .usage("Project name")
57                        .required(),
58                    Flag::choice("template", &["basic", "web", "api", "full"])
59                        .usage("Project template to use")
60                        .default_str("basic"),
61                    Flag::directory("path")
62                        .short('p')
63                        .usage("Directory to create project in")
64                        .completion(|_ctx, prefix| {
65                            // In a real app, list actual directories
66                            let dirs = vec!["./", "../", "/tmp/", "~/projects/"];
67                            Ok(flag_rs::CompletionResult::new().extend(
68                                dirs.into_iter()
69                                    .filter(|d| d.starts_with(prefix))
70                                    .map(String::from),
71                            ))
72                        }),
73                ])
74                .run(|ctx| {
75                    // Type-safe flag access
76                    let name = ctx.flag_str_or("name", "my-project");
77                    let template = ctx.flag_str_or("template", "basic");
78                    let path = ctx.flag_str_or("path", ".");
79                    let verbose = ctx.flag_bool_or("verbose", false);
80
81                    if verbose {
82                        println!("Initializing {} project '{}' in {}", template, name, path);
83                    } else {
84                        println!("Creating project '{}'...", name);
85                    }
86
87                    Ok(())
88                })
89                .build(),
90            CommandBuilder::new("build")
91                .short("Build the project")
92                .flags(vec![
93                    Flag::bool("release")
94                        .short('r')
95                        .usage("Build in release mode"),
96                    Flag::string_slice("features")
97                        .short('f')
98                        .usage("Enable features (can be specified multiple times)")
99                        .completion(|_ctx, prefix| {
100                            let features = vec!["async", "tls", "compression", "metrics"];
101                            Ok(flag_rs::CompletionResult::new().extend(
102                                features
103                                    .into_iter()
104                                    .filter(|f| f.starts_with(prefix))
105                                    .map(String::from),
106                            ))
107                        }),
108                    Flag::range("jobs", 1, 32)
109                        .short('j')
110                        .usage("Number of parallel jobs")
111                        .default_int(i64::try_from(num_cpus()).unwrap_or(4)),
112                ])
113                .run(|ctx| {
114                    let release = ctx.flag_bool_or("release", false);
115                    let jobs = ctx.flag_int_or("jobs", i64::try_from(num_cpus()).unwrap_or(4));
116                    let quiet = ctx.flag_bool_or("quiet", false);
117
118                    if !quiet {
119                        println!(
120                            "Building in {} mode with {} jobs",
121                            if release { "release" } else { "debug" },
122                            jobs
123                        );
124
125                        if let Some(features) = ctx.flag("features") {
126                            println!("Features: {}", features);
127                        }
128                    }
129
130                    Ok(())
131                })
132                .build(),
133            CommandBuilder::new("test")
134                .short("Run tests")
135                .flags(vec![
136                    Flag::string("filter").usage("Only run tests matching this pattern"),
137                    Flag::bool("nocapture").usage("Don't capture test output"),
138                    Flag::int("test-threads")
139                        .usage("Number of threads to use for running tests")
140                        .default_int(1),
141                ])
142                .run(|ctx| {
143                    let threads = ctx.flag_int_or("test-threads", 1);
144                    let nocapture = ctx.flag_bool_or("nocapture", false);
145                    let verbose = ctx.flag_bool_or("verbose", false);
146
147                    if verbose {
148                        println!("Running tests with {} thread(s)", threads);
149                        if nocapture {
150                            println!("Output capture disabled");
151                        }
152
153                        if let Some(filter) = ctx.flag("filter") {
154                            println!("Filter: {}", filter);
155                        }
156                    }
157
158                    println!("Running tests...");
159                    Ok(())
160                })
161                .build(),
162        ])
163        .run(|_ctx| {
164            // Root command - show help by default
165            println!("Use --help for usage information");
166            Ok(())
167        })
168        .build();
169
170    let args: Vec<String> = std::env::args().skip(1).collect();
171    if let Err(e) = app.execute(args) {
172        eprintln!("Error: {}", e);
173        std::process::exit(1);
174    }
175}
176
177// Dummy function for example
178fn num_cpus() -> usize {
179    std::thread::available_parallelism()
180        .map(std::num::NonZero::get)
181        .unwrap_or(4)
182}