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