builder_api_demo/
builder_api_demo.rs1use flag_rs::{CommandBuilder, Flag};
3
4fn main() {
5 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 .flags(vec![
17 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 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 .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 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 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 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
177fn num_cpus() -> usize {
179 std::thread::available_parallelism()
180 .map(std::num::NonZero::get)
181 .unwrap_or(4)
182}