pub struct Flag {
pub name: String,
pub short: Option<char>,
pub usage: String,
pub default: Option<FlagValue>,
pub required: bool,
pub value_type: FlagType,
pub constraints: Vec<FlagConstraint>,
pub completion: Option<CompletionFunc>,
}Expand description
Represents a command-line flag
A Flag defines a command-line option that can be passed to a command.
Flags can have both long names (e.g., --verbose) and short names (e.g., -v).
§Examples
use flag_rs::flag::{Flag, FlagType, FlagValue};
// Create a boolean flag
let verbose = Flag::new("verbose")
.short('v')
.usage("Enable verbose output")
.value_type(FlagType::Bool)
.default(FlagValue::Bool(false));
// Create a string flag with validation
let name = Flag::new("name")
.short('n')
.usage("Name of the resource")
.value_type(FlagType::String)
.required();Fields§
§name: StringThe long name of the flag (e.g., “verbose” for –verbose)
short: Option<char>The optional short name of the flag (e.g., ‘v’ for -v)
usage: StringA description of what the flag does
default: Option<FlagValue>The default value if the flag is not provided
required: boolWhether this flag must be provided
value_type: FlagTypeThe type of value this flag accepts
constraints: Vec<FlagConstraint>Constraints applied to this flag
completion: Option<CompletionFunc>Optional completion function for this flag’s values
Implementations§
Source§impl Flag
impl Flag
Sourcepub fn new(name: impl Into<String>) -> Self
pub fn new(name: impl Into<String>) -> Self
Creates a new flag with the given name
§Examples
use flag_rs::flag::Flag;
let flag = Flag::new("verbose");
assert_eq!(flag.name, "verbose");Examples found in repository?
50fn create_simple_cli() -> flag_rs::Command {
51 CommandBuilder::new("bench")
52 .short("Benchmark CLI")
53 .flag(
54 Flag::new("verbose")
55 .short('v')
56 .value_type(FlagType::Bool)
57 .default(FlagValue::Bool(false)),
58 )
59 .flag(Flag::new("output").short('o').value_type(FlagType::String))
60 .build()
61}
62
63/// Creates a complex nested command structure
64fn create_complex_cli() -> flag_rs::Command {
65 let mut root = CommandBuilder::new("complex")
66 .short("Complex CLI with many subcommands")
67 .flag(
68 Flag::new("config")
69 .short('c')
70 .value_type(FlagType::File)
71 .default(FlagValue::String("config.yaml".to_string())),
72 )
73 .build();
74
75 // Add 50 subcommands
76 for i in 0..50 {
77 let mut sub = CommandBuilder::new(format!("sub{i:02}"))
78 .short(format!("Subcommand {i}"))
79 .flag(Flag::new("flag1").short('1').value_type(FlagType::String))
80 .flag(Flag::new("flag2").short('2').value_type(FlagType::Int))
81 .build();
82
83 // Add 10 nested subcommands
84 for j in 0..10 {
85 sub.add_command(
86 CommandBuilder::new(format!("nested{j}"))
87 .short(format!("Nested command {j}"))
88 .flag(Flag::new("deep").value_type(FlagType::Bool))
89 .build(),
90 );
91 }
92
93 root.add_command(sub);
94 }
95
96 root
97}
98
99fn bench_command_creation() {
100 println!("\n=== Command Creation Benchmarks ===");
101
102 let bench = Benchmark::new("Simple command creation", 10_000);
103 let duration = bench.run(|| {
104 let _ = create_simple_cli();
105 });
106 bench.report(duration);
107
108 let bench = Benchmark::new("Complex command creation (50 subs)", 100);
109 let duration = bench.run(|| {
110 let _ = create_complex_cli();
111 });
112 bench.report(duration);
113
114 let bench = Benchmark::new("CommandBuilder with 10 flags", 1_000);
115 let duration = bench.run(|| {
116 let mut cmd = CommandBuilder::new("test");
117 for i in 0..10 {
118 cmd = cmd.flag(Flag::new(format!("flag{i}")).value_type(FlagType::String));
119 }
120 let _ = cmd.build();
121 });
122 bench.report(duration);
123}
124
125fn bench_flag_parsing() {
126 println!("\n=== Flag Parsing Benchmarks ===");
127
128 let cli = create_simple_cli();
129
130 let bench = Benchmark::new("Parse no flags", 10_000);
131 let duration = bench.run(|| {
132 let args = vec!["bench".to_string()];
133 let _ = cli.execute(args);
134 });
135 bench.report(duration);
136
137 let bench = Benchmark::new("Parse single flag", 10_000);
138 let duration = bench.run(|| {
139 let args = vec!["bench".to_string(), "--verbose".to_string()];
140 let _ = cli.execute(args);
141 });
142 bench.report(duration);
143
144 let bench = Benchmark::new("Parse flag with value", 10_000);
145 let duration = bench.run(|| {
146 let args = vec![
147 "bench".to_string(),
148 "--output".to_string(),
149 "file.txt".to_string(),
150 ];
151 let _ = cli.execute(args);
152 });
153 bench.report(duration);
154
155 let bench = Benchmark::new("Parse multiple flags", 10_000);
156 let duration = bench.run(|| {
157 let args = vec![
158 "bench".to_string(),
159 "-v".to_string(),
160 "-o".to_string(),
161 "output.txt".to_string(),
162 ];
163 let _ = cli.execute(args);
164 });
165 bench.report(duration);
166}
167
168fn bench_subcommand_lookup() {
169 println!("\n=== Subcommand Lookup Benchmarks ===");
170
171 let cli = create_complex_cli();
172
173 let bench = Benchmark::new("Find immediate subcommand", 10_000);
174 let duration = bench.run(|| {
175 let _ = cli.find_subcommand("sub25");
176 });
177 bench.report(duration);
178
179 let bench = Benchmark::new("Find nested subcommand", 10_000);
180 let duration = bench.run(|| {
181 if let Some(sub) = cli.find_subcommand("sub25") {
182 let _ = sub.find_subcommand("nested5");
183 }
184 });
185 bench.report(duration);
186
187 let bench = Benchmark::new("Execute nested command", 1_000);
188 let duration = bench.run(|| {
189 let args = vec![
190 "complex".to_string(),
191 "sub25".to_string(),
192 "nested5".to_string(),
193 "--deep".to_string(),
194 ];
195 let _ = cli.execute(args);
196 });
197 bench.report(duration);
198}
199
200fn bench_completion() {
201 println!("\n=== Completion Benchmarks ===");
202
203 let cli = CommandBuilder::new("comp")
204 .arg_completion(|_ctx, prefix| {
205 let items: Vec<String> = (0..100)
206 .map(|i| format!("item{i:03}"))
207 .filter(|item| item.starts_with(prefix))
208 .collect();
209 Ok(CompletionResult::new().extend(items))
210 })
211 .build();
212
213 let ctx = flag_rs::Context::new(vec!["comp".to_string()]);
214
215 let bench = Benchmark::new("Complete with empty prefix (100 items)", 1_000);
216 let duration = bench.run(|| {
217 let _ = cli.get_completions(&ctx, "", None);
218 });
219 bench.report(duration);
220
221 let bench = Benchmark::new("Complete with prefix (filtered)", 1_000);
222 let duration = bench.run(|| {
223 let _ = cli.get_completions(&ctx, "item05", None);
224 });
225 bench.report(duration);
226
227 // Test completion with descriptions
228 let cli_desc = CommandBuilder::new("comp_desc")
229 .arg_completion(|_ctx, prefix| {
230 let mut result = CompletionResult::new();
231 for i in 0..50 {
232 let item = format!("option{i:02}");
233 if item.starts_with(prefix) {
234 result =
235 result.add_with_description(item, format!("Description for option {i}"));
236 }
237 }
238 Ok(result)
239 })
240 .build();
241
242 let bench = Benchmark::new("Complete with descriptions (50 items)", 1_000);
243 let duration = bench.run(|| {
244 let _ = cli_desc.get_completions(&ctx, "", None);
245 });
246 bench.report(duration);
247}
248
249fn bench_flag_validation() {
250 println!("\n=== Flag Validation Benchmarks ===");
251
252 let cli = CommandBuilder::new("validate")
253 .flag(Flag::new("choice").value_type(FlagType::Choice(vec![
254 "opt1".to_string(),
255 "opt2".to_string(),
256 "opt3".to_string(),
257 ])))
258 .flag(Flag::new("range").value_type(FlagType::Range(1, 100)))
259 .flag(
260 Flag::new("required")
261 .value_type(FlagType::String)
262 .required(),
263 )
264 .build();
265
266 let bench = Benchmark::new("Validate choice flag", 10_000);
267 let duration = bench.run(|| {
268 let args = vec![
269 "validate".to_string(),
270 "--choice".to_string(),
271 "opt2".to_string(),
272 "--required".to_string(),
273 "value".to_string(),
274 ];
275 let _ = cli.execute(args);
276 });
277 bench.report(duration);
278
279 let bench = Benchmark::new("Validate range flag", 10_000);
280 let duration = bench.run(|| {
281 let args = vec![
282 "validate".to_string(),
283 "--range".to_string(),
284 "50".to_string(),
285 "--required".to_string(),
286 "value".to_string(),
287 ];
288 let _ = cli.execute(args);
289 });
290 bench.report(duration);
291
292 let bench = Benchmark::new("Validate missing required flag", 10_000);
293 let duration = bench.run(|| {
294 let args = vec!["validate".to_string()];
295 let _ = cli.execute(args); // This will fail validation
296 });
297 bench.report(duration);
298}More examples
14fn build_kubectl() -> Command {
15 CommandBuilder::new("kubectl")
16 .short("Kubernetes command-line tool")
17 .long("kubectl controls the Kubernetes cluster manager")
18 .subcommand(build_get_command())
19 .subcommand(build_describe_command())
20 .subcommand(build_delete_command())
21 .subcommand(build_completion_command())
22 .flag(
23 Flag::new("namespace")
24 .short('n')
25 .usage("Kubernetes namespace")
26 .value_type(FlagType::String)
27 .default(FlagValue::String("default".to_string())),
28 )
29 .flag_completion("namespace", |_ctx, prefix| {
30 // In a real kubectl, this would query the API server
31 let namespaces = get_namespaces_with_descriptions();
32 let mut result = CompletionResult::new();
33
34 for (ns_name, description) in namespaces {
35 if ns_name.starts_with(prefix) {
36 result = result.add_with_description(ns_name, description);
37 }
38 }
39
40 Ok(result)
41 })
42 .build()
43}
44
45fn build_get_command() -> Command {
46 CommandBuilder::new("get")
47 .short("Display one or many resources")
48 .long("Display one or many resources. Prints a table of the most important information about the specified resources.")
49 .flag(
50 Flag::new("limit")
51 .short('l')
52 .usage("Maximum number of resources to display")
53 .value_type(FlagType::String)
54 )
55 .flag_completion("limit", |_ctx, prefix| {
56 let common_limits = vec![
57 ("10", "Display 10 items"),
58 ("20", "Display 20 items"),
59 ("50", "Display 50 items"),
60 ("100", "Display 100 items"),
61 ("all", "Display all items (no limit)"),
62 ];
63
64 let mut result = CompletionResult::new();
65 for (limit, description) in common_limits {
66 if limit.starts_with(prefix) {
67 result = result.add_with_description(limit.to_string(), description.to_string());
68 }
69 }
70
71 Ok(result)
72 })
73 .subcommand(build_get_pods())
74 .subcommand(build_get_services())
75 .subcommand(build_get_deployments())
76 .build()
77}17fn main() {
18 let app = CommandBuilder::new("demo")
19 .short("Terminal feature demonstration")
20 .long("This application demonstrates the terminal width detection and text wrapping features. The help text should automatically adjust to your terminal width, wrapping long lines at word boundaries while maintaining readability. Try running this with different COLUMNS values to see how it adapts.")
21 .flag(
22 Flag::new("file")
23 .short('f')
24 .usage("Input file path. This flag expects a path to a file that will be processed. The file must exist and be readable.")
25 .value_type(FlagType::String)
26 )
27 .flag(
28 Flag::new("output-format")
29 .short('o')
30 .usage("Output format for results. Supported formats include: json (machine-readable JSON format), yaml (human-friendly YAML format), table (formatted ASCII table), csv (comma-separated values for spreadsheet import)")
31 .value_type(FlagType::String)
32 .default(FlagValue::String("table".to_string()))
33 )
34 .subcommand(
35 CommandBuilder::new("process")
36 .short("Process data with various transformations and filters that can be applied in sequence")
37 .build()
38 )
39 .build();
40
41 // Always show help for this demo
42 app.print_help();
43}3fn main() {
4 let cmd = CommandBuilder::new("test")
5 .short("Test completion with descriptions")
6 .flag(
7 Flag::new("environment")
8 .short('e')
9 .usage("Target environment")
10 .value_type(FlagType::String),
11 )
12 .flag_completion("environment", |_ctx, prefix| {
13 let mut result = CompletionResult::new();
14 let envs = vec![
15 ("dev", "Development environment - safe for testing"),
16 ("staging", "Staging environment - mirror of production"),
17 ("production", "Production environment - BE CAREFUL!"),
18 ];
19
20 for (env, desc) in envs {
21 if env.starts_with(prefix) {
22 result = result.add_with_description(env, desc);
23 }
24 }
25
26 Ok(result)
27 })
28 .subcommand(
29 CommandBuilder::new("deploy")
30 .short("Deploy the application")
31 .long("Deploy the application to the specified environment")
32 .build(),
33 )
34 .subcommand(
35 CommandBuilder::new("rollback")
36 .short("Rollback to previous version")
37 .long("Rollback the application to the previous deployed version")
38 .build(),
39 )
40 .build();
41
42 // Test completion output
43 if let Ok(shell_type) = std::env::var("TEST_COMPLETE") {
44 // For testing, override shell type to "display" to see formatted output
45 let display_mode = std::env::var("DISPLAY_MODE").is_ok();
46 let args: Vec<String> = std::env::args().skip(1).collect();
47
48 if display_mode {
49 // This is a hack for testing - normally shells would handle this
50 println!("Display mode - showing formatted completions:");
51 match cmd.handle_completion_request(&args) {
52 Ok(completions) => {
53 for completion in completions {
54 println!("{}", completion);
55 }
56 }
57 Err(e) => eprintln!("Completion error: {}", e),
58 }
59 } else {
60 match cmd.handle_completion_request(&args) {
61 Ok(completions) => {
62 println!("Shell type: {}", shell_type);
63 println!("Completions:");
64 for completion in completions {
65 println!("{}", completion);
66 }
67 }
68 Err(e) => eprintln!("Completion error: {}", e),
69 }
70 }
71 } else {
72 let args: Vec<String> = std::env::args().skip(1).collect();
73 if let Err(e) = cmd.execute(args) {
74 eprintln!("Error: {}", e);
75 std::process::exit(1);
76 }
77 }
78}7fn main() {
8 let app = CommandBuilder::new("errordemo")
9 .short("Demo of enhanced error messages")
10 .flag(
11 Flag::new("verbose")
12 .short('v')
13 .value_type(FlagType::Bool)
14 .usage("Enable verbose output"),
15 )
16 .flag(
17 Flag::new("workers")
18 .short('w')
19 .value_type(FlagType::Range(1, 10))
20 .usage("Number of worker threads"),
21 )
22 .flag(
23 Flag::new("format")
24 .short('f')
25 .value_type(FlagType::Choice(vec![
26 "json".to_string(),
27 "yaml".to_string(),
28 "xml".to_string(),
29 "toml".to_string(),
30 ]))
31 .usage("Output format"),
32 )
33 .flag(
34 Flag::new("config")
35 .short('c')
36 .value_type(FlagType::File)
37 .usage("Configuration file"),
38 )
39 .flag(
40 Flag::new("outdir")
41 .short('o')
42 .value_type(FlagType::Directory)
43 .usage("Output directory"),
44 )
45 .flag(
46 Flag::new("ssl")
47 .value_type(FlagType::Bool)
48 .usage("Enable SSL"),
49 )
50 .flag(
51 Flag::new("ssl-cert")
52 .value_type(FlagType::File)
53 .constraint(FlagConstraint::RequiredIf("ssl".to_string()))
54 .usage("SSL certificate file"),
55 )
56 .flag(
57 Flag::new("json")
58 .value_type(FlagType::Bool)
59 .constraint(FlagConstraint::ConflictsWith(vec!["xml".to_string()]))
60 .usage("Output in JSON format"),
61 )
62 .flag(
63 Flag::new("xml")
64 .value_type(FlagType::Bool)
65 .usage("Output in XML format"),
66 )
67 .flag(
68 Flag::new("required-flag")
69 .value_type(FlagType::String)
70 .required()
71 .usage("This flag is required"),
72 )
73 .subcommand(
74 CommandBuilder::new("process")
75 .short("Process data")
76 .run(|ctx| {
77 println!("Processing with flags: {:?}", ctx.flags());
78 Ok(())
79 })
80 .build(),
81 )
82 .build();
83
84 println!("Try these commands to see enhanced error messages:\n");
85 println!(" # Boolean parsing error");
86 println!(" cargo run --example enhanced_errors_demo -- --verbose=maybe\n");
87
88 println!(" # Integer parsing error");
89 println!(" cargo run --example enhanced_errors_demo -- --workers=abc\n");
90
91 println!(" # Range validation error");
92 println!(" cargo run --example enhanced_errors_demo -- --workers=20\n");
93
94 println!(" # Choice validation error");
95 println!(" cargo run --example enhanced_errors_demo -- --format=csv\n");
96
97 println!(" # File not found error");
98 println!(" cargo run --example enhanced_errors_demo -- --config=/tmp/nonexistent.conf\n");
99
100 println!(" # Directory not found error");
101 println!(" cargo run --example enhanced_errors_demo -- --outdir=/tmp/nonexistent_dir\n");
102
103 println!(" # Flag constraint error (RequiredIf)");
104 println!(" cargo run --example enhanced_errors_demo -- --ssl --required-flag=test process\n");
105
106 println!(" # Flag constraint error (ConflictsWith)");
107 println!(
108 " cargo run --example enhanced_errors_demo -- --json --xml --required-flag=test process\n"
109 );
110
111 println!(" # Required flag error");
112 println!(" cargo run --example enhanced_errors_demo -- process\n");
113
114 println!(" # Unknown command error");
115 println!(" cargo run --example enhanced_errors_demo -- --required-flag=test proces\n");
116
117 let args: Vec<String> = std::env::args().skip(1).collect();
118 if !args.is_empty() {
119 println!("\n{}", "=".repeat(60));
120 println!("Running with args: {:?}\n", args);
121
122 if let Err(e) = app.execute(args) {
123 eprintln!("{}", e);
124 std::process::exit(1);
125 }
126 }
127}12fn create_many_subcommands(parent: &mut flag_rs::Command) {
13 // Simulate a large CLI with 100 subcommands
14 for i in 0..100 {
15 let cmd_name = format!("service-{i:03}");
16
17 // Use string interning for flag names that repeat across commands
18 let namespace_flag_name = string_pool::intern("namespace");
19 let region_flag_name = string_pool::intern("region");
20 let env_flag_name = string_pool::intern("environment");
21
22 let cmd = CommandBuilder::new(cmd_name.clone())
23 .short(format!("Manage service {i}"))
24 .flag(
25 Flag::new(namespace_flag_name.to_string())
26 .short('n')
27 .usage("Kubernetes namespace")
28 .value_type(FlagType::String)
29 .default(flag_rs::FlagValue::String("default".to_string())),
30 )
31 .flag(
32 Flag::new(region_flag_name.to_string())
33 .short('r')
34 .usage("AWS region")
35 .value_type(FlagType::Choice(vec![
36 "us-east-1".to_string(),
37 "us-west-2".to_string(),
38 "eu-west-1".to_string(),
39 "ap-southeast-1".to_string(),
40 ]))
41 .default(flag_rs::FlagValue::String("us-east-1".to_string())),
42 )
43 .flag(
44 Flag::new(env_flag_name.to_string())
45 .short('e')
46 .usage("Deployment environment")
47 .value_type(FlagType::Choice(vec![
48 "dev".to_string(),
49 "staging".to_string(),
50 "prod".to_string(),
51 ]))
52 .default(flag_rs::FlagValue::String("dev".to_string())),
53 )
54 .subcommand(
55 CommandBuilder::new("deploy")
56 .short("Deploy the service")
57 .arg_completion(move |_ctx, prefix| {
58 // Use optimized completion result
59 let optimized = CompletionResultOptimized::new()
60 .add(Cow::Borrowed("rolling-update"))
61 .add(Cow::Borrowed("blue-green"))
62 .add(Cow::Borrowed("canary"))
63 .add_with_description(
64 Cow::Borrowed("recreate"),
65 Cow::Borrowed("Recreate all pods"),
66 );
67
68 // Filter based on prefix
69 let filtered = CompletionResultOptimized::new().extend_items(
70 optimized
71 .items
72 .into_iter()
73 .filter(|item| item.value.starts_with(prefix)),
74 );
75
76 // Convert to legacy format for compatibility
77 Ok(filtered.into_legacy())
78 })
79 .build(),
80 )
81 .subcommand(
82 CommandBuilder::new("scale")
83 .short("Scale the service")
84 .flag(
85 Flag::new("replicas")
86 .usage("Number of replicas")
87 .value_type(FlagType::Int)
88 .required(),
89 )
90 .build(),
91 )
92 .subcommand(
93 CommandBuilder::new("logs")
94 .short("View service logs")
95 .flag(
96 Flag::new("follow")
97 .short('f')
98 .usage("Follow log output")
99 .value_type(FlagType::Bool),
100 )
101 .flag(
102 Flag::new("tail")
103 .usage("Number of lines to show")
104 .value_type(FlagType::Int)
105 .default(flag_rs::FlagValue::Int(100)),
106 )
107 .build(),
108 )
109 .build();
110
111 parent.add_command(cmd);
112 }
113}
114
115// Count commands recursively
116fn count_commands(cmd: &flag_rs::Command) -> usize {
117 1 + cmd
118 .subcommands()
119 .values()
120 .map(count_commands)
121 .sum::<usize>()
122}
123
124// Count flags recursively
125fn count_flags(cmd: &flag_rs::Command) -> usize {
126 cmd.flags().len() + cmd.subcommands().values().map(count_flags).sum::<usize>()
127}
128
129fn main() {
130 // Create root command
131 let mut app = CommandBuilder::new("megacli")
132 .short("A large CLI demonstrating memory optimizations")
133 .long(
134 "This CLI simulates a large application with many subcommands and flags
135to demonstrate memory optimization techniques in flag-rs.
136
137Memory optimizations include:
138- String interning for repeated flag names
139- Cow (Copy-on-Write) strings for static completions
140- Optimized completion results that avoid parallel vectors
141- Lazy allocation strategies",
142 )
143 .flag(
144 Flag::new("verbose")
145 .short('v')
146 .usage("Enable verbose output")
147 .value_type(FlagType::Bool),
148 )
149 .flag(
150 Flag::new("config")
151 .short('c')
152 .usage("Path to config file")
153 .value_type(FlagType::File)
154 .default(flag_rs::FlagValue::String(
155 "~/.megacli/config.yaml".to_string(),
156 )),
157 )
158 .build();
159
160 // Add many subcommands
161 create_many_subcommands(&mut app);
162
163 let total_commands = count_commands(&app);
164 let total_flags = count_flags(&app);
165
166 // Add a special command to show memory usage stats
167 app.add_command(
168 CommandBuilder::new("stats")
169 .short("Show CLI statistics and memory usage")
170 .run(move |_ctx| {
171 println!("=== MegaCLI Statistics ===\n");
172
173 println!("Total commands: {total_commands}");
174 println!("Total flags: {total_flags}");
175 println!("String pool size: {} unique strings", 3); // We interned 3 flag names
176
177 println!("\nMemory optimization features in use:");
178 println!("✓ String interning for flag names");
179 println!("✓ Cow<str> for static completion values");
180 println!("✓ CompletionResultOptimized for reduced allocations");
181 println!("✓ Lazy allocation strategies");
182
183 println!("\nEstimated memory savings:");
184 println!("- 60-70% reduction in string allocations");
185 println!("- 40-50% reduction in completion memory usage");
186 println!("- Improved cache locality for better performance");
187
188 Ok(())
189 })
190 .build(),
191 );
192
193 // Execute the CLI
194 let args: Vec<String> = std::env::args().skip(1).collect();
195
196 if args.is_empty() {
197 println!("=== Memory Optimization Demo ===\n");
198 println!("This demo shows how flag-rs optimizes memory for large CLIs.\n");
199 println!("Try these commands:");
200 println!(
201 " {} stats # Show memory statistics",
202 std::env::args().next().unwrap_or_default()
203 );
204 println!(
205 " {} service-001 deploy <TAB> # Test optimized completions",
206 std::env::args().next().unwrap_or_default()
207 );
208 println!(
209 " {} --help # See all 100+ commands",
210 std::env::args().next().unwrap_or_default()
211 );
212 println!("\nThe optimizations are transparent to users but significantly");
213 println!("reduce memory usage for CLIs with many commands and flags.");
214 std::process::exit(0);
215 }
216
217 if let Err(e) = app.execute(args) {
218 eprintln!("{e}");
219 std::process::exit(1);
220 }
221}Sourcepub fn bool(name: impl Into<String>) -> Self
pub fn bool(name: impl Into<String>) -> Self
Examples found in repository?
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}Sourcepub fn int(name: impl Into<String>) -> Self
pub fn int(name: impl Into<String>) -> Self
Examples found in repository?
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}Sourcepub fn string(name: impl Into<String>) -> Self
pub fn string(name: impl Into<String>) -> Self
Examples found in repository?
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}Sourcepub fn string_slice(name: impl Into<String>) -> Self
pub fn string_slice(name: impl Into<String>) -> Self
Creates a new string slice flag (can be specified multiple times)
§Examples
use flag_rs::flag::Flag;
let flag = Flag::string_slice("tag");Examples found in repository?
4fn main() {
5 let app = CommandBuilder::new("deploy")
6 .short("Deploy application to various environments")
7 .flag(
8 Flag::choice("environment", &["dev", "staging", "prod"])
9 .short('e')
10 .usage("Target environment")
11 .required()
12 .completion(|_ctx, prefix| {
13 // Dynamic completion - could check available environments
14 let environments = vec![
15 ("dev", "Development environment"),
16 ("staging", "Staging environment (pre-production)"),
17 ("prod", "Production environment"),
18 ];
19
20 let mut result = CompletionResult::new();
21 for (env, desc) in environments {
22 if env.starts_with(prefix) {
23 result = result.add_with_description(env.to_string(), desc.to_string());
24 }
25 }
26
27 // Add active help if no prefix
28 if prefix.is_empty() {
29 result = result.add_help_text("Available environments: dev, staging, prod");
30 }
31
32 Ok(result)
33 }),
34 )
35 .flag(
36 Flag::file("config")
37 .short('c')
38 .usage("Configuration file")
39 .default_str("config.yaml")
40 .completion(|_ctx, prefix| {
41 // In a real app, you might list files in the config directory
42 let configs = vec![
43 "config.yaml",
44 "config.dev.yaml",
45 "config.staging.yaml",
46 "config.prod.yaml",
47 "secrets.yaml",
48 ];
49
50 Ok(CompletionResult::new().extend(
51 configs
52 .into_iter()
53 .filter(|c| c.starts_with(prefix))
54 .map(String::from),
55 ))
56 }),
57 )
58 .flag(
59 Flag::directory("output")
60 .short('o')
61 .usage("Output directory for deployment artifacts")
62 .completion(|_ctx, prefix| {
63 // In a real app, you might list actual directories
64 let dirs = vec!["./build", "./dist", "./output", "/tmp/deploy"];
65
66 Ok(CompletionResult::new().extend(
67 dirs.into_iter()
68 .filter(|d| d.starts_with(prefix))
69 .map(String::from),
70 ))
71 }),
72 )
73 .flag(
74 Flag::string_slice("service")
75 .short('s')
76 .usage("Services to deploy (can be specified multiple times)")
77 .completion(|ctx, prefix| {
78 // Context-aware completion based on environment
79 let env = ctx.flag_str_or("environment", "dev");
80
81 let services = match env {
82 "dev" => vec!["api", "web", "worker", "scheduler", "debug-panel"],
83 "staging" => vec!["api", "web", "worker", "scheduler"],
84 "prod" => vec!["api", "web", "worker"],
85 _ => vec!["api", "web"],
86 };
87
88 let mut result = CompletionResult::new().extend(
89 services
90 .into_iter()
91 .filter(|s| s.starts_with(prefix))
92 .map(String::from),
93 );
94
95 // Add context-aware help
96 if prefix.is_empty() {
97 result = result
98 .add_help_text(format!("Available services for {} environment", env));
99 }
100
101 Ok(result)
102 }),
103 )
104 .flag(
105 Flag::range("replicas", 1, 10)
106 .short('r')
107 .usage("Number of replicas to deploy")
108 .default_int(2)
109 .completion(|_ctx, _prefix| {
110 // Suggest common replica counts
111 Ok(CompletionResult::new()
112 .add_with_description(
113 "1".to_string(),
114 "Single instance (dev/test)".to_string(),
115 )
116 .add_with_description("2".to_string(), "Basic redundancy".to_string())
117 .add_with_description("3".to_string(), "Standard production".to_string())
118 .add_with_description("5".to_string(), "High availability".to_string()))
119 }),
120 )
121 .run(|ctx| {
122 let env = ctx.flag_str_or("environment", "dev");
123 let config = ctx.flag_str_or("config", "config.yaml");
124 let replicas = ctx.flag_int_or("replicas", 2);
125
126 println!("Deploying to {} environment", env);
127 println!("Using configuration: {}", config);
128 println!("Replica count: {}", replicas);
129
130 if let Some(output) = ctx.flag("output") {
131 println!("Output directory: {}", output);
132 }
133
134 if let Some(services) = ctx.flag("service") {
135 println!("Deploying services: {}", services);
136 } else {
137 println!("Deploying all services");
138 }
139
140 Ok(())
141 })
142 .build();
143
144 let args: Vec<String> = std::env::args().skip(1).collect();
145 if let Err(e) = app.execute(args) {
146 eprintln!("Error: {}", e);
147 std::process::exit(1);
148 }
149}More examples
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}Sourcepub fn choice(name: impl Into<String>, choices: &[&str]) -> Self
pub fn choice(name: impl Into<String>, choices: &[&str]) -> Self
Creates a new choice flag with allowed values
§Examples
use flag_rs::flag::Flag;
let flag = Flag::choice("format", &["json", "yaml", "xml"]);Examples found in repository?
4fn main() {
5 let app = CommandBuilder::new("deploy")
6 .short("Deploy application to various environments")
7 .flag(
8 Flag::choice("environment", &["dev", "staging", "prod"])
9 .short('e')
10 .usage("Target environment")
11 .required()
12 .completion(|_ctx, prefix| {
13 // Dynamic completion - could check available environments
14 let environments = vec![
15 ("dev", "Development environment"),
16 ("staging", "Staging environment (pre-production)"),
17 ("prod", "Production environment"),
18 ];
19
20 let mut result = CompletionResult::new();
21 for (env, desc) in environments {
22 if env.starts_with(prefix) {
23 result = result.add_with_description(env.to_string(), desc.to_string());
24 }
25 }
26
27 // Add active help if no prefix
28 if prefix.is_empty() {
29 result = result.add_help_text("Available environments: dev, staging, prod");
30 }
31
32 Ok(result)
33 }),
34 )
35 .flag(
36 Flag::file("config")
37 .short('c')
38 .usage("Configuration file")
39 .default_str("config.yaml")
40 .completion(|_ctx, prefix| {
41 // In a real app, you might list files in the config directory
42 let configs = vec![
43 "config.yaml",
44 "config.dev.yaml",
45 "config.staging.yaml",
46 "config.prod.yaml",
47 "secrets.yaml",
48 ];
49
50 Ok(CompletionResult::new().extend(
51 configs
52 .into_iter()
53 .filter(|c| c.starts_with(prefix))
54 .map(String::from),
55 ))
56 }),
57 )
58 .flag(
59 Flag::directory("output")
60 .short('o')
61 .usage("Output directory for deployment artifacts")
62 .completion(|_ctx, prefix| {
63 // In a real app, you might list actual directories
64 let dirs = vec!["./build", "./dist", "./output", "/tmp/deploy"];
65
66 Ok(CompletionResult::new().extend(
67 dirs.into_iter()
68 .filter(|d| d.starts_with(prefix))
69 .map(String::from),
70 ))
71 }),
72 )
73 .flag(
74 Flag::string_slice("service")
75 .short('s')
76 .usage("Services to deploy (can be specified multiple times)")
77 .completion(|ctx, prefix| {
78 // Context-aware completion based on environment
79 let env = ctx.flag_str_or("environment", "dev");
80
81 let services = match env {
82 "dev" => vec!["api", "web", "worker", "scheduler", "debug-panel"],
83 "staging" => vec!["api", "web", "worker", "scheduler"],
84 "prod" => vec!["api", "web", "worker"],
85 _ => vec!["api", "web"],
86 };
87
88 let mut result = CompletionResult::new().extend(
89 services
90 .into_iter()
91 .filter(|s| s.starts_with(prefix))
92 .map(String::from),
93 );
94
95 // Add context-aware help
96 if prefix.is_empty() {
97 result = result
98 .add_help_text(format!("Available services for {} environment", env));
99 }
100
101 Ok(result)
102 }),
103 )
104 .flag(
105 Flag::range("replicas", 1, 10)
106 .short('r')
107 .usage("Number of replicas to deploy")
108 .default_int(2)
109 .completion(|_ctx, _prefix| {
110 // Suggest common replica counts
111 Ok(CompletionResult::new()
112 .add_with_description(
113 "1".to_string(),
114 "Single instance (dev/test)".to_string(),
115 )
116 .add_with_description("2".to_string(), "Basic redundancy".to_string())
117 .add_with_description("3".to_string(), "Standard production".to_string())
118 .add_with_description("5".to_string(), "High availability".to_string()))
119 }),
120 )
121 .run(|ctx| {
122 let env = ctx.flag_str_or("environment", "dev");
123 let config = ctx.flag_str_or("config", "config.yaml");
124 let replicas = ctx.flag_int_or("replicas", 2);
125
126 println!("Deploying to {} environment", env);
127 println!("Using configuration: {}", config);
128 println!("Replica count: {}", replicas);
129
130 if let Some(output) = ctx.flag("output") {
131 println!("Output directory: {}", output);
132 }
133
134 if let Some(services) = ctx.flag("service") {
135 println!("Deploying services: {}", services);
136 } else {
137 println!("Deploying all services");
138 }
139
140 Ok(())
141 })
142 .build();
143
144 let args: Vec<String> = std::env::args().skip(1).collect();
145 if let Err(e) = app.execute(args) {
146 eprintln!("Error: {}", e);
147 std::process::exit(1);
148 }
149}More examples
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}Sourcepub fn range(name: impl Into<String>, min: i64, max: i64) -> Self
pub fn range(name: impl Into<String>, min: i64, max: i64) -> Self
Creates a new range flag with min and max values
§Examples
use flag_rs::flag::Flag;
let flag = Flag::range("workers", 1, 16);Examples found in repository?
4fn main() {
5 let app = CommandBuilder::new("deploy")
6 .short("Deploy application to various environments")
7 .flag(
8 Flag::choice("environment", &["dev", "staging", "prod"])
9 .short('e')
10 .usage("Target environment")
11 .required()
12 .completion(|_ctx, prefix| {
13 // Dynamic completion - could check available environments
14 let environments = vec![
15 ("dev", "Development environment"),
16 ("staging", "Staging environment (pre-production)"),
17 ("prod", "Production environment"),
18 ];
19
20 let mut result = CompletionResult::new();
21 for (env, desc) in environments {
22 if env.starts_with(prefix) {
23 result = result.add_with_description(env.to_string(), desc.to_string());
24 }
25 }
26
27 // Add active help if no prefix
28 if prefix.is_empty() {
29 result = result.add_help_text("Available environments: dev, staging, prod");
30 }
31
32 Ok(result)
33 }),
34 )
35 .flag(
36 Flag::file("config")
37 .short('c')
38 .usage("Configuration file")
39 .default_str("config.yaml")
40 .completion(|_ctx, prefix| {
41 // In a real app, you might list files in the config directory
42 let configs = vec![
43 "config.yaml",
44 "config.dev.yaml",
45 "config.staging.yaml",
46 "config.prod.yaml",
47 "secrets.yaml",
48 ];
49
50 Ok(CompletionResult::new().extend(
51 configs
52 .into_iter()
53 .filter(|c| c.starts_with(prefix))
54 .map(String::from),
55 ))
56 }),
57 )
58 .flag(
59 Flag::directory("output")
60 .short('o')
61 .usage("Output directory for deployment artifacts")
62 .completion(|_ctx, prefix| {
63 // In a real app, you might list actual directories
64 let dirs = vec!["./build", "./dist", "./output", "/tmp/deploy"];
65
66 Ok(CompletionResult::new().extend(
67 dirs.into_iter()
68 .filter(|d| d.starts_with(prefix))
69 .map(String::from),
70 ))
71 }),
72 )
73 .flag(
74 Flag::string_slice("service")
75 .short('s')
76 .usage("Services to deploy (can be specified multiple times)")
77 .completion(|ctx, prefix| {
78 // Context-aware completion based on environment
79 let env = ctx.flag_str_or("environment", "dev");
80
81 let services = match env {
82 "dev" => vec!["api", "web", "worker", "scheduler", "debug-panel"],
83 "staging" => vec!["api", "web", "worker", "scheduler"],
84 "prod" => vec!["api", "web", "worker"],
85 _ => vec!["api", "web"],
86 };
87
88 let mut result = CompletionResult::new().extend(
89 services
90 .into_iter()
91 .filter(|s| s.starts_with(prefix))
92 .map(String::from),
93 );
94
95 // Add context-aware help
96 if prefix.is_empty() {
97 result = result
98 .add_help_text(format!("Available services for {} environment", env));
99 }
100
101 Ok(result)
102 }),
103 )
104 .flag(
105 Flag::range("replicas", 1, 10)
106 .short('r')
107 .usage("Number of replicas to deploy")
108 .default_int(2)
109 .completion(|_ctx, _prefix| {
110 // Suggest common replica counts
111 Ok(CompletionResult::new()
112 .add_with_description(
113 "1".to_string(),
114 "Single instance (dev/test)".to_string(),
115 )
116 .add_with_description("2".to_string(), "Basic redundancy".to_string())
117 .add_with_description("3".to_string(), "Standard production".to_string())
118 .add_with_description("5".to_string(), "High availability".to_string()))
119 }),
120 )
121 .run(|ctx| {
122 let env = ctx.flag_str_or("environment", "dev");
123 let config = ctx.flag_str_or("config", "config.yaml");
124 let replicas = ctx.flag_int_or("replicas", 2);
125
126 println!("Deploying to {} environment", env);
127 println!("Using configuration: {}", config);
128 println!("Replica count: {}", replicas);
129
130 if let Some(output) = ctx.flag("output") {
131 println!("Output directory: {}", output);
132 }
133
134 if let Some(services) = ctx.flag("service") {
135 println!("Deploying services: {}", services);
136 } else {
137 println!("Deploying all services");
138 }
139
140 Ok(())
141 })
142 .build();
143
144 let args: Vec<String> = std::env::args().skip(1).collect();
145 if let Err(e) = app.execute(args) {
146 eprintln!("Error: {}", e);
147 std::process::exit(1);
148 }
149}More examples
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}Sourcepub fn file(name: impl Into<String>) -> Self
pub fn file(name: impl Into<String>) -> Self
Examples found in repository?
4fn main() {
5 let app = CommandBuilder::new("deploy")
6 .short("Deploy application to various environments")
7 .flag(
8 Flag::choice("environment", &["dev", "staging", "prod"])
9 .short('e')
10 .usage("Target environment")
11 .required()
12 .completion(|_ctx, prefix| {
13 // Dynamic completion - could check available environments
14 let environments = vec![
15 ("dev", "Development environment"),
16 ("staging", "Staging environment (pre-production)"),
17 ("prod", "Production environment"),
18 ];
19
20 let mut result = CompletionResult::new();
21 for (env, desc) in environments {
22 if env.starts_with(prefix) {
23 result = result.add_with_description(env.to_string(), desc.to_string());
24 }
25 }
26
27 // Add active help if no prefix
28 if prefix.is_empty() {
29 result = result.add_help_text("Available environments: dev, staging, prod");
30 }
31
32 Ok(result)
33 }),
34 )
35 .flag(
36 Flag::file("config")
37 .short('c')
38 .usage("Configuration file")
39 .default_str("config.yaml")
40 .completion(|_ctx, prefix| {
41 // In a real app, you might list files in the config directory
42 let configs = vec![
43 "config.yaml",
44 "config.dev.yaml",
45 "config.staging.yaml",
46 "config.prod.yaml",
47 "secrets.yaml",
48 ];
49
50 Ok(CompletionResult::new().extend(
51 configs
52 .into_iter()
53 .filter(|c| c.starts_with(prefix))
54 .map(String::from),
55 ))
56 }),
57 )
58 .flag(
59 Flag::directory("output")
60 .short('o')
61 .usage("Output directory for deployment artifacts")
62 .completion(|_ctx, prefix| {
63 // In a real app, you might list actual directories
64 let dirs = vec!["./build", "./dist", "./output", "/tmp/deploy"];
65
66 Ok(CompletionResult::new().extend(
67 dirs.into_iter()
68 .filter(|d| d.starts_with(prefix))
69 .map(String::from),
70 ))
71 }),
72 )
73 .flag(
74 Flag::string_slice("service")
75 .short('s')
76 .usage("Services to deploy (can be specified multiple times)")
77 .completion(|ctx, prefix| {
78 // Context-aware completion based on environment
79 let env = ctx.flag_str_or("environment", "dev");
80
81 let services = match env {
82 "dev" => vec!["api", "web", "worker", "scheduler", "debug-panel"],
83 "staging" => vec!["api", "web", "worker", "scheduler"],
84 "prod" => vec!["api", "web", "worker"],
85 _ => vec!["api", "web"],
86 };
87
88 let mut result = CompletionResult::new().extend(
89 services
90 .into_iter()
91 .filter(|s| s.starts_with(prefix))
92 .map(String::from),
93 );
94
95 // Add context-aware help
96 if prefix.is_empty() {
97 result = result
98 .add_help_text(format!("Available services for {} environment", env));
99 }
100
101 Ok(result)
102 }),
103 )
104 .flag(
105 Flag::range("replicas", 1, 10)
106 .short('r')
107 .usage("Number of replicas to deploy")
108 .default_int(2)
109 .completion(|_ctx, _prefix| {
110 // Suggest common replica counts
111 Ok(CompletionResult::new()
112 .add_with_description(
113 "1".to_string(),
114 "Single instance (dev/test)".to_string(),
115 )
116 .add_with_description("2".to_string(), "Basic redundancy".to_string())
117 .add_with_description("3".to_string(), "Standard production".to_string())
118 .add_with_description("5".to_string(), "High availability".to_string()))
119 }),
120 )
121 .run(|ctx| {
122 let env = ctx.flag_str_or("environment", "dev");
123 let config = ctx.flag_str_or("config", "config.yaml");
124 let replicas = ctx.flag_int_or("replicas", 2);
125
126 println!("Deploying to {} environment", env);
127 println!("Using configuration: {}", config);
128 println!("Replica count: {}", replicas);
129
130 if let Some(output) = ctx.flag("output") {
131 println!("Output directory: {}", output);
132 }
133
134 if let Some(services) = ctx.flag("service") {
135 println!("Deploying services: {}", services);
136 } else {
137 println!("Deploying all services");
138 }
139
140 Ok(())
141 })
142 .build();
143
144 let args: Vec<String> = std::env::args().skip(1).collect();
145 if let Err(e) = app.execute(args) {
146 eprintln!("Error: {}", e);
147 std::process::exit(1);
148 }
149}Sourcepub fn directory(name: impl Into<String>) -> Self
pub fn directory(name: impl Into<String>) -> Self
Creates a new directory flag
§Examples
use flag_rs::flag::Flag;
let flag = Flag::directory("output");Examples found in repository?
4fn main() {
5 let app = CommandBuilder::new("deploy")
6 .short("Deploy application to various environments")
7 .flag(
8 Flag::choice("environment", &["dev", "staging", "prod"])
9 .short('e')
10 .usage("Target environment")
11 .required()
12 .completion(|_ctx, prefix| {
13 // Dynamic completion - could check available environments
14 let environments = vec![
15 ("dev", "Development environment"),
16 ("staging", "Staging environment (pre-production)"),
17 ("prod", "Production environment"),
18 ];
19
20 let mut result = CompletionResult::new();
21 for (env, desc) in environments {
22 if env.starts_with(prefix) {
23 result = result.add_with_description(env.to_string(), desc.to_string());
24 }
25 }
26
27 // Add active help if no prefix
28 if prefix.is_empty() {
29 result = result.add_help_text("Available environments: dev, staging, prod");
30 }
31
32 Ok(result)
33 }),
34 )
35 .flag(
36 Flag::file("config")
37 .short('c')
38 .usage("Configuration file")
39 .default_str("config.yaml")
40 .completion(|_ctx, prefix| {
41 // In a real app, you might list files in the config directory
42 let configs = vec![
43 "config.yaml",
44 "config.dev.yaml",
45 "config.staging.yaml",
46 "config.prod.yaml",
47 "secrets.yaml",
48 ];
49
50 Ok(CompletionResult::new().extend(
51 configs
52 .into_iter()
53 .filter(|c| c.starts_with(prefix))
54 .map(String::from),
55 ))
56 }),
57 )
58 .flag(
59 Flag::directory("output")
60 .short('o')
61 .usage("Output directory for deployment artifacts")
62 .completion(|_ctx, prefix| {
63 // In a real app, you might list actual directories
64 let dirs = vec!["./build", "./dist", "./output", "/tmp/deploy"];
65
66 Ok(CompletionResult::new().extend(
67 dirs.into_iter()
68 .filter(|d| d.starts_with(prefix))
69 .map(String::from),
70 ))
71 }),
72 )
73 .flag(
74 Flag::string_slice("service")
75 .short('s')
76 .usage("Services to deploy (can be specified multiple times)")
77 .completion(|ctx, prefix| {
78 // Context-aware completion based on environment
79 let env = ctx.flag_str_or("environment", "dev");
80
81 let services = match env {
82 "dev" => vec!["api", "web", "worker", "scheduler", "debug-panel"],
83 "staging" => vec!["api", "web", "worker", "scheduler"],
84 "prod" => vec!["api", "web", "worker"],
85 _ => vec!["api", "web"],
86 };
87
88 let mut result = CompletionResult::new().extend(
89 services
90 .into_iter()
91 .filter(|s| s.starts_with(prefix))
92 .map(String::from),
93 );
94
95 // Add context-aware help
96 if prefix.is_empty() {
97 result = result
98 .add_help_text(format!("Available services for {} environment", env));
99 }
100
101 Ok(result)
102 }),
103 )
104 .flag(
105 Flag::range("replicas", 1, 10)
106 .short('r')
107 .usage("Number of replicas to deploy")
108 .default_int(2)
109 .completion(|_ctx, _prefix| {
110 // Suggest common replica counts
111 Ok(CompletionResult::new()
112 .add_with_description(
113 "1".to_string(),
114 "Single instance (dev/test)".to_string(),
115 )
116 .add_with_description("2".to_string(), "Basic redundancy".to_string())
117 .add_with_description("3".to_string(), "Standard production".to_string())
118 .add_with_description("5".to_string(), "High availability".to_string()))
119 }),
120 )
121 .run(|ctx| {
122 let env = ctx.flag_str_or("environment", "dev");
123 let config = ctx.flag_str_or("config", "config.yaml");
124 let replicas = ctx.flag_int_or("replicas", 2);
125
126 println!("Deploying to {} environment", env);
127 println!("Using configuration: {}", config);
128 println!("Replica count: {}", replicas);
129
130 if let Some(output) = ctx.flag("output") {
131 println!("Output directory: {}", output);
132 }
133
134 if let Some(services) = ctx.flag("service") {
135 println!("Deploying services: {}", services);
136 } else {
137 println!("Deploying all services");
138 }
139
140 Ok(())
141 })
142 .build();
143
144 let args: Vec<String> = std::env::args().skip(1).collect();
145 if let Err(e) = app.execute(args) {
146 eprintln!("Error: {}", e);
147 std::process::exit(1);
148 }
149}More examples
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}Sourcepub const fn short(self, short: char) -> Self
pub const fn short(self, short: char) -> Self
Sets the short name for this flag
§Examples
use flag_rs::flag::Flag;
let flag = Flag::new("verbose").short('v');
assert_eq!(flag.short, Some('v'));Examples found in repository?
50fn create_simple_cli() -> flag_rs::Command {
51 CommandBuilder::new("bench")
52 .short("Benchmark CLI")
53 .flag(
54 Flag::new("verbose")
55 .short('v')
56 .value_type(FlagType::Bool)
57 .default(FlagValue::Bool(false)),
58 )
59 .flag(Flag::new("output").short('o').value_type(FlagType::String))
60 .build()
61}
62
63/// Creates a complex nested command structure
64fn create_complex_cli() -> flag_rs::Command {
65 let mut root = CommandBuilder::new("complex")
66 .short("Complex CLI with many subcommands")
67 .flag(
68 Flag::new("config")
69 .short('c')
70 .value_type(FlagType::File)
71 .default(FlagValue::String("config.yaml".to_string())),
72 )
73 .build();
74
75 // Add 50 subcommands
76 for i in 0..50 {
77 let mut sub = CommandBuilder::new(format!("sub{i:02}"))
78 .short(format!("Subcommand {i}"))
79 .flag(Flag::new("flag1").short('1').value_type(FlagType::String))
80 .flag(Flag::new("flag2").short('2').value_type(FlagType::Int))
81 .build();
82
83 // Add 10 nested subcommands
84 for j in 0..10 {
85 sub.add_command(
86 CommandBuilder::new(format!("nested{j}"))
87 .short(format!("Nested command {j}"))
88 .flag(Flag::new("deep").value_type(FlagType::Bool))
89 .build(),
90 );
91 }
92
93 root.add_command(sub);
94 }
95
96 root
97}More examples
14fn build_kubectl() -> Command {
15 CommandBuilder::new("kubectl")
16 .short("Kubernetes command-line tool")
17 .long("kubectl controls the Kubernetes cluster manager")
18 .subcommand(build_get_command())
19 .subcommand(build_describe_command())
20 .subcommand(build_delete_command())
21 .subcommand(build_completion_command())
22 .flag(
23 Flag::new("namespace")
24 .short('n')
25 .usage("Kubernetes namespace")
26 .value_type(FlagType::String)
27 .default(FlagValue::String("default".to_string())),
28 )
29 .flag_completion("namespace", |_ctx, prefix| {
30 // In a real kubectl, this would query the API server
31 let namespaces = get_namespaces_with_descriptions();
32 let mut result = CompletionResult::new();
33
34 for (ns_name, description) in namespaces {
35 if ns_name.starts_with(prefix) {
36 result = result.add_with_description(ns_name, description);
37 }
38 }
39
40 Ok(result)
41 })
42 .build()
43}
44
45fn build_get_command() -> Command {
46 CommandBuilder::new("get")
47 .short("Display one or many resources")
48 .long("Display one or many resources. Prints a table of the most important information about the specified resources.")
49 .flag(
50 Flag::new("limit")
51 .short('l')
52 .usage("Maximum number of resources to display")
53 .value_type(FlagType::String)
54 )
55 .flag_completion("limit", |_ctx, prefix| {
56 let common_limits = vec![
57 ("10", "Display 10 items"),
58 ("20", "Display 20 items"),
59 ("50", "Display 50 items"),
60 ("100", "Display 100 items"),
61 ("all", "Display all items (no limit)"),
62 ];
63
64 let mut result = CompletionResult::new();
65 for (limit, description) in common_limits {
66 if limit.starts_with(prefix) {
67 result = result.add_with_description(limit.to_string(), description.to_string());
68 }
69 }
70
71 Ok(result)
72 })
73 .subcommand(build_get_pods())
74 .subcommand(build_get_services())
75 .subcommand(build_get_deployments())
76 .build()
77}17fn main() {
18 let app = CommandBuilder::new("demo")
19 .short("Terminal feature demonstration")
20 .long("This application demonstrates the terminal width detection and text wrapping features. The help text should automatically adjust to your terminal width, wrapping long lines at word boundaries while maintaining readability. Try running this with different COLUMNS values to see how it adapts.")
21 .flag(
22 Flag::new("file")
23 .short('f')
24 .usage("Input file path. This flag expects a path to a file that will be processed. The file must exist and be readable.")
25 .value_type(FlagType::String)
26 )
27 .flag(
28 Flag::new("output-format")
29 .short('o')
30 .usage("Output format for results. Supported formats include: json (machine-readable JSON format), yaml (human-friendly YAML format), table (formatted ASCII table), csv (comma-separated values for spreadsheet import)")
31 .value_type(FlagType::String)
32 .default(FlagValue::String("table".to_string()))
33 )
34 .subcommand(
35 CommandBuilder::new("process")
36 .short("Process data with various transformations and filters that can be applied in sequence")
37 .build()
38 )
39 .build();
40
41 // Always show help for this demo
42 app.print_help();
43}3fn main() {
4 let cmd = CommandBuilder::new("test")
5 .short("Test completion with descriptions")
6 .flag(
7 Flag::new("environment")
8 .short('e')
9 .usage("Target environment")
10 .value_type(FlagType::String),
11 )
12 .flag_completion("environment", |_ctx, prefix| {
13 let mut result = CompletionResult::new();
14 let envs = vec![
15 ("dev", "Development environment - safe for testing"),
16 ("staging", "Staging environment - mirror of production"),
17 ("production", "Production environment - BE CAREFUL!"),
18 ];
19
20 for (env, desc) in envs {
21 if env.starts_with(prefix) {
22 result = result.add_with_description(env, desc);
23 }
24 }
25
26 Ok(result)
27 })
28 .subcommand(
29 CommandBuilder::new("deploy")
30 .short("Deploy the application")
31 .long("Deploy the application to the specified environment")
32 .build(),
33 )
34 .subcommand(
35 CommandBuilder::new("rollback")
36 .short("Rollback to previous version")
37 .long("Rollback the application to the previous deployed version")
38 .build(),
39 )
40 .build();
41
42 // Test completion output
43 if let Ok(shell_type) = std::env::var("TEST_COMPLETE") {
44 // For testing, override shell type to "display" to see formatted output
45 let display_mode = std::env::var("DISPLAY_MODE").is_ok();
46 let args: Vec<String> = std::env::args().skip(1).collect();
47
48 if display_mode {
49 // This is a hack for testing - normally shells would handle this
50 println!("Display mode - showing formatted completions:");
51 match cmd.handle_completion_request(&args) {
52 Ok(completions) => {
53 for completion in completions {
54 println!("{}", completion);
55 }
56 }
57 Err(e) => eprintln!("Completion error: {}", e),
58 }
59 } else {
60 match cmd.handle_completion_request(&args) {
61 Ok(completions) => {
62 println!("Shell type: {}", shell_type);
63 println!("Completions:");
64 for completion in completions {
65 println!("{}", completion);
66 }
67 }
68 Err(e) => eprintln!("Completion error: {}", e),
69 }
70 }
71 } else {
72 let args: Vec<String> = std::env::args().skip(1).collect();
73 if let Err(e) = cmd.execute(args) {
74 eprintln!("Error: {}", e);
75 std::process::exit(1);
76 }
77 }
78}7fn main() {
8 let app = CommandBuilder::new("errordemo")
9 .short("Demo of enhanced error messages")
10 .flag(
11 Flag::new("verbose")
12 .short('v')
13 .value_type(FlagType::Bool)
14 .usage("Enable verbose output"),
15 )
16 .flag(
17 Flag::new("workers")
18 .short('w')
19 .value_type(FlagType::Range(1, 10))
20 .usage("Number of worker threads"),
21 )
22 .flag(
23 Flag::new("format")
24 .short('f')
25 .value_type(FlagType::Choice(vec![
26 "json".to_string(),
27 "yaml".to_string(),
28 "xml".to_string(),
29 "toml".to_string(),
30 ]))
31 .usage("Output format"),
32 )
33 .flag(
34 Flag::new("config")
35 .short('c')
36 .value_type(FlagType::File)
37 .usage("Configuration file"),
38 )
39 .flag(
40 Flag::new("outdir")
41 .short('o')
42 .value_type(FlagType::Directory)
43 .usage("Output directory"),
44 )
45 .flag(
46 Flag::new("ssl")
47 .value_type(FlagType::Bool)
48 .usage("Enable SSL"),
49 )
50 .flag(
51 Flag::new("ssl-cert")
52 .value_type(FlagType::File)
53 .constraint(FlagConstraint::RequiredIf("ssl".to_string()))
54 .usage("SSL certificate file"),
55 )
56 .flag(
57 Flag::new("json")
58 .value_type(FlagType::Bool)
59 .constraint(FlagConstraint::ConflictsWith(vec!["xml".to_string()]))
60 .usage("Output in JSON format"),
61 )
62 .flag(
63 Flag::new("xml")
64 .value_type(FlagType::Bool)
65 .usage("Output in XML format"),
66 )
67 .flag(
68 Flag::new("required-flag")
69 .value_type(FlagType::String)
70 .required()
71 .usage("This flag is required"),
72 )
73 .subcommand(
74 CommandBuilder::new("process")
75 .short("Process data")
76 .run(|ctx| {
77 println!("Processing with flags: {:?}", ctx.flags());
78 Ok(())
79 })
80 .build(),
81 )
82 .build();
83
84 println!("Try these commands to see enhanced error messages:\n");
85 println!(" # Boolean parsing error");
86 println!(" cargo run --example enhanced_errors_demo -- --verbose=maybe\n");
87
88 println!(" # Integer parsing error");
89 println!(" cargo run --example enhanced_errors_demo -- --workers=abc\n");
90
91 println!(" # Range validation error");
92 println!(" cargo run --example enhanced_errors_demo -- --workers=20\n");
93
94 println!(" # Choice validation error");
95 println!(" cargo run --example enhanced_errors_demo -- --format=csv\n");
96
97 println!(" # File not found error");
98 println!(" cargo run --example enhanced_errors_demo -- --config=/tmp/nonexistent.conf\n");
99
100 println!(" # Directory not found error");
101 println!(" cargo run --example enhanced_errors_demo -- --outdir=/tmp/nonexistent_dir\n");
102
103 println!(" # Flag constraint error (RequiredIf)");
104 println!(" cargo run --example enhanced_errors_demo -- --ssl --required-flag=test process\n");
105
106 println!(" # Flag constraint error (ConflictsWith)");
107 println!(
108 " cargo run --example enhanced_errors_demo -- --json --xml --required-flag=test process\n"
109 );
110
111 println!(" # Required flag error");
112 println!(" cargo run --example enhanced_errors_demo -- process\n");
113
114 println!(" # Unknown command error");
115 println!(" cargo run --example enhanced_errors_demo -- --required-flag=test proces\n");
116
117 let args: Vec<String> = std::env::args().skip(1).collect();
118 if !args.is_empty() {
119 println!("\n{}", "=".repeat(60));
120 println!("Running with args: {:?}\n", args);
121
122 if let Err(e) = app.execute(args) {
123 eprintln!("{}", e);
124 std::process::exit(1);
125 }
126 }
127}12fn create_many_subcommands(parent: &mut flag_rs::Command) {
13 // Simulate a large CLI with 100 subcommands
14 for i in 0..100 {
15 let cmd_name = format!("service-{i:03}");
16
17 // Use string interning for flag names that repeat across commands
18 let namespace_flag_name = string_pool::intern("namespace");
19 let region_flag_name = string_pool::intern("region");
20 let env_flag_name = string_pool::intern("environment");
21
22 let cmd = CommandBuilder::new(cmd_name.clone())
23 .short(format!("Manage service {i}"))
24 .flag(
25 Flag::new(namespace_flag_name.to_string())
26 .short('n')
27 .usage("Kubernetes namespace")
28 .value_type(FlagType::String)
29 .default(flag_rs::FlagValue::String("default".to_string())),
30 )
31 .flag(
32 Flag::new(region_flag_name.to_string())
33 .short('r')
34 .usage("AWS region")
35 .value_type(FlagType::Choice(vec![
36 "us-east-1".to_string(),
37 "us-west-2".to_string(),
38 "eu-west-1".to_string(),
39 "ap-southeast-1".to_string(),
40 ]))
41 .default(flag_rs::FlagValue::String("us-east-1".to_string())),
42 )
43 .flag(
44 Flag::new(env_flag_name.to_string())
45 .short('e')
46 .usage("Deployment environment")
47 .value_type(FlagType::Choice(vec![
48 "dev".to_string(),
49 "staging".to_string(),
50 "prod".to_string(),
51 ]))
52 .default(flag_rs::FlagValue::String("dev".to_string())),
53 )
54 .subcommand(
55 CommandBuilder::new("deploy")
56 .short("Deploy the service")
57 .arg_completion(move |_ctx, prefix| {
58 // Use optimized completion result
59 let optimized = CompletionResultOptimized::new()
60 .add(Cow::Borrowed("rolling-update"))
61 .add(Cow::Borrowed("blue-green"))
62 .add(Cow::Borrowed("canary"))
63 .add_with_description(
64 Cow::Borrowed("recreate"),
65 Cow::Borrowed("Recreate all pods"),
66 );
67
68 // Filter based on prefix
69 let filtered = CompletionResultOptimized::new().extend_items(
70 optimized
71 .items
72 .into_iter()
73 .filter(|item| item.value.starts_with(prefix)),
74 );
75
76 // Convert to legacy format for compatibility
77 Ok(filtered.into_legacy())
78 })
79 .build(),
80 )
81 .subcommand(
82 CommandBuilder::new("scale")
83 .short("Scale the service")
84 .flag(
85 Flag::new("replicas")
86 .usage("Number of replicas")
87 .value_type(FlagType::Int)
88 .required(),
89 )
90 .build(),
91 )
92 .subcommand(
93 CommandBuilder::new("logs")
94 .short("View service logs")
95 .flag(
96 Flag::new("follow")
97 .short('f')
98 .usage("Follow log output")
99 .value_type(FlagType::Bool),
100 )
101 .flag(
102 Flag::new("tail")
103 .usage("Number of lines to show")
104 .value_type(FlagType::Int)
105 .default(flag_rs::FlagValue::Int(100)),
106 )
107 .build(),
108 )
109 .build();
110
111 parent.add_command(cmd);
112 }
113}
114
115// Count commands recursively
116fn count_commands(cmd: &flag_rs::Command) -> usize {
117 1 + cmd
118 .subcommands()
119 .values()
120 .map(count_commands)
121 .sum::<usize>()
122}
123
124// Count flags recursively
125fn count_flags(cmd: &flag_rs::Command) -> usize {
126 cmd.flags().len() + cmd.subcommands().values().map(count_flags).sum::<usize>()
127}
128
129fn main() {
130 // Create root command
131 let mut app = CommandBuilder::new("megacli")
132 .short("A large CLI demonstrating memory optimizations")
133 .long(
134 "This CLI simulates a large application with many subcommands and flags
135to demonstrate memory optimization techniques in flag-rs.
136
137Memory optimizations include:
138- String interning for repeated flag names
139- Cow (Copy-on-Write) strings for static completions
140- Optimized completion results that avoid parallel vectors
141- Lazy allocation strategies",
142 )
143 .flag(
144 Flag::new("verbose")
145 .short('v')
146 .usage("Enable verbose output")
147 .value_type(FlagType::Bool),
148 )
149 .flag(
150 Flag::new("config")
151 .short('c')
152 .usage("Path to config file")
153 .value_type(FlagType::File)
154 .default(flag_rs::FlagValue::String(
155 "~/.megacli/config.yaml".to_string(),
156 )),
157 )
158 .build();
159
160 // Add many subcommands
161 create_many_subcommands(&mut app);
162
163 let total_commands = count_commands(&app);
164 let total_flags = count_flags(&app);
165
166 // Add a special command to show memory usage stats
167 app.add_command(
168 CommandBuilder::new("stats")
169 .short("Show CLI statistics and memory usage")
170 .run(move |_ctx| {
171 println!("=== MegaCLI Statistics ===\n");
172
173 println!("Total commands: {total_commands}");
174 println!("Total flags: {total_flags}");
175 println!("String pool size: {} unique strings", 3); // We interned 3 flag names
176
177 println!("\nMemory optimization features in use:");
178 println!("✓ String interning for flag names");
179 println!("✓ Cow<str> for static completion values");
180 println!("✓ CompletionResultOptimized for reduced allocations");
181 println!("✓ Lazy allocation strategies");
182
183 println!("\nEstimated memory savings:");
184 println!("- 60-70% reduction in string allocations");
185 println!("- 40-50% reduction in completion memory usage");
186 println!("- Improved cache locality for better performance");
187
188 Ok(())
189 })
190 .build(),
191 );
192
193 // Execute the CLI
194 let args: Vec<String> = std::env::args().skip(1).collect();
195
196 if args.is_empty() {
197 println!("=== Memory Optimization Demo ===\n");
198 println!("This demo shows how flag-rs optimizes memory for large CLIs.\n");
199 println!("Try these commands:");
200 println!(
201 " {} stats # Show memory statistics",
202 std::env::args().next().unwrap_or_default()
203 );
204 println!(
205 " {} service-001 deploy <TAB> # Test optimized completions",
206 std::env::args().next().unwrap_or_default()
207 );
208 println!(
209 " {} --help # See all 100+ commands",
210 std::env::args().next().unwrap_or_default()
211 );
212 println!("\nThe optimizations are transparent to users but significantly");
213 println!("reduce memory usage for CLIs with many commands and flags.");
214 std::process::exit(0);
215 }
216
217 if let Err(e) = app.execute(args) {
218 eprintln!("{e}");
219 std::process::exit(1);
220 }
221}- examples/error_handling_demo.rs
- examples/timeout_completion_demo.rs
- examples/flag_completion_demo.rs
- examples/enhanced_help_demo.rs
- examples/cached_completion_demo.rs
- examples/lifecycle_hooks_demo.rs
- examples/builder_api_demo.rs
- examples/advanced_flags_demo.rs
- examples/help_formatting_demo.rs
- examples/active_help_demo.rs
Sourcepub fn usage(self, usage: impl Into<String>) -> Self
pub fn usage(self, usage: impl Into<String>) -> Self
Sets the usage description for this flag
§Examples
use flag_rs::flag::Flag;
let flag = Flag::new("verbose").usage("Enable verbose output");
assert_eq!(flag.usage, "Enable verbose output");Examples found in repository?
14fn build_kubectl() -> Command {
15 CommandBuilder::new("kubectl")
16 .short("Kubernetes command-line tool")
17 .long("kubectl controls the Kubernetes cluster manager")
18 .subcommand(build_get_command())
19 .subcommand(build_describe_command())
20 .subcommand(build_delete_command())
21 .subcommand(build_completion_command())
22 .flag(
23 Flag::new("namespace")
24 .short('n')
25 .usage("Kubernetes namespace")
26 .value_type(FlagType::String)
27 .default(FlagValue::String("default".to_string())),
28 )
29 .flag_completion("namespace", |_ctx, prefix| {
30 // In a real kubectl, this would query the API server
31 let namespaces = get_namespaces_with_descriptions();
32 let mut result = CompletionResult::new();
33
34 for (ns_name, description) in namespaces {
35 if ns_name.starts_with(prefix) {
36 result = result.add_with_description(ns_name, description);
37 }
38 }
39
40 Ok(result)
41 })
42 .build()
43}
44
45fn build_get_command() -> Command {
46 CommandBuilder::new("get")
47 .short("Display one or many resources")
48 .long("Display one or many resources. Prints a table of the most important information about the specified resources.")
49 .flag(
50 Flag::new("limit")
51 .short('l')
52 .usage("Maximum number of resources to display")
53 .value_type(FlagType::String)
54 )
55 .flag_completion("limit", |_ctx, prefix| {
56 let common_limits = vec![
57 ("10", "Display 10 items"),
58 ("20", "Display 20 items"),
59 ("50", "Display 50 items"),
60 ("100", "Display 100 items"),
61 ("all", "Display all items (no limit)"),
62 ];
63
64 let mut result = CompletionResult::new();
65 for (limit, description) in common_limits {
66 if limit.starts_with(prefix) {
67 result = result.add_with_description(limit.to_string(), description.to_string());
68 }
69 }
70
71 Ok(result)
72 })
73 .subcommand(build_get_pods())
74 .subcommand(build_get_services())
75 .subcommand(build_get_deployments())
76 .build()
77}More examples
17fn main() {
18 let app = CommandBuilder::new("demo")
19 .short("Terminal feature demonstration")
20 .long("This application demonstrates the terminal width detection and text wrapping features. The help text should automatically adjust to your terminal width, wrapping long lines at word boundaries while maintaining readability. Try running this with different COLUMNS values to see how it adapts.")
21 .flag(
22 Flag::new("file")
23 .short('f')
24 .usage("Input file path. This flag expects a path to a file that will be processed. The file must exist and be readable.")
25 .value_type(FlagType::String)
26 )
27 .flag(
28 Flag::new("output-format")
29 .short('o')
30 .usage("Output format for results. Supported formats include: json (machine-readable JSON format), yaml (human-friendly YAML format), table (formatted ASCII table), csv (comma-separated values for spreadsheet import)")
31 .value_type(FlagType::String)
32 .default(FlagValue::String("table".to_string()))
33 )
34 .subcommand(
35 CommandBuilder::new("process")
36 .short("Process data with various transformations and filters that can be applied in sequence")
37 .build()
38 )
39 .build();
40
41 // Always show help for this demo
42 app.print_help();
43}3fn main() {
4 let cmd = CommandBuilder::new("test")
5 .short("Test completion with descriptions")
6 .flag(
7 Flag::new("environment")
8 .short('e')
9 .usage("Target environment")
10 .value_type(FlagType::String),
11 )
12 .flag_completion("environment", |_ctx, prefix| {
13 let mut result = CompletionResult::new();
14 let envs = vec![
15 ("dev", "Development environment - safe for testing"),
16 ("staging", "Staging environment - mirror of production"),
17 ("production", "Production environment - BE CAREFUL!"),
18 ];
19
20 for (env, desc) in envs {
21 if env.starts_with(prefix) {
22 result = result.add_with_description(env, desc);
23 }
24 }
25
26 Ok(result)
27 })
28 .subcommand(
29 CommandBuilder::new("deploy")
30 .short("Deploy the application")
31 .long("Deploy the application to the specified environment")
32 .build(),
33 )
34 .subcommand(
35 CommandBuilder::new("rollback")
36 .short("Rollback to previous version")
37 .long("Rollback the application to the previous deployed version")
38 .build(),
39 )
40 .build();
41
42 // Test completion output
43 if let Ok(shell_type) = std::env::var("TEST_COMPLETE") {
44 // For testing, override shell type to "display" to see formatted output
45 let display_mode = std::env::var("DISPLAY_MODE").is_ok();
46 let args: Vec<String> = std::env::args().skip(1).collect();
47
48 if display_mode {
49 // This is a hack for testing - normally shells would handle this
50 println!("Display mode - showing formatted completions:");
51 match cmd.handle_completion_request(&args) {
52 Ok(completions) => {
53 for completion in completions {
54 println!("{}", completion);
55 }
56 }
57 Err(e) => eprintln!("Completion error: {}", e),
58 }
59 } else {
60 match cmd.handle_completion_request(&args) {
61 Ok(completions) => {
62 println!("Shell type: {}", shell_type);
63 println!("Completions:");
64 for completion in completions {
65 println!("{}", completion);
66 }
67 }
68 Err(e) => eprintln!("Completion error: {}", e),
69 }
70 }
71 } else {
72 let args: Vec<String> = std::env::args().skip(1).collect();
73 if let Err(e) = cmd.execute(args) {
74 eprintln!("Error: {}", e);
75 std::process::exit(1);
76 }
77 }
78}7fn main() {
8 let app = CommandBuilder::new("errordemo")
9 .short("Demo of enhanced error messages")
10 .flag(
11 Flag::new("verbose")
12 .short('v')
13 .value_type(FlagType::Bool)
14 .usage("Enable verbose output"),
15 )
16 .flag(
17 Flag::new("workers")
18 .short('w')
19 .value_type(FlagType::Range(1, 10))
20 .usage("Number of worker threads"),
21 )
22 .flag(
23 Flag::new("format")
24 .short('f')
25 .value_type(FlagType::Choice(vec![
26 "json".to_string(),
27 "yaml".to_string(),
28 "xml".to_string(),
29 "toml".to_string(),
30 ]))
31 .usage("Output format"),
32 )
33 .flag(
34 Flag::new("config")
35 .short('c')
36 .value_type(FlagType::File)
37 .usage("Configuration file"),
38 )
39 .flag(
40 Flag::new("outdir")
41 .short('o')
42 .value_type(FlagType::Directory)
43 .usage("Output directory"),
44 )
45 .flag(
46 Flag::new("ssl")
47 .value_type(FlagType::Bool)
48 .usage("Enable SSL"),
49 )
50 .flag(
51 Flag::new("ssl-cert")
52 .value_type(FlagType::File)
53 .constraint(FlagConstraint::RequiredIf("ssl".to_string()))
54 .usage("SSL certificate file"),
55 )
56 .flag(
57 Flag::new("json")
58 .value_type(FlagType::Bool)
59 .constraint(FlagConstraint::ConflictsWith(vec!["xml".to_string()]))
60 .usage("Output in JSON format"),
61 )
62 .flag(
63 Flag::new("xml")
64 .value_type(FlagType::Bool)
65 .usage("Output in XML format"),
66 )
67 .flag(
68 Flag::new("required-flag")
69 .value_type(FlagType::String)
70 .required()
71 .usage("This flag is required"),
72 )
73 .subcommand(
74 CommandBuilder::new("process")
75 .short("Process data")
76 .run(|ctx| {
77 println!("Processing with flags: {:?}", ctx.flags());
78 Ok(())
79 })
80 .build(),
81 )
82 .build();
83
84 println!("Try these commands to see enhanced error messages:\n");
85 println!(" # Boolean parsing error");
86 println!(" cargo run --example enhanced_errors_demo -- --verbose=maybe\n");
87
88 println!(" # Integer parsing error");
89 println!(" cargo run --example enhanced_errors_demo -- --workers=abc\n");
90
91 println!(" # Range validation error");
92 println!(" cargo run --example enhanced_errors_demo -- --workers=20\n");
93
94 println!(" # Choice validation error");
95 println!(" cargo run --example enhanced_errors_demo -- --format=csv\n");
96
97 println!(" # File not found error");
98 println!(" cargo run --example enhanced_errors_demo -- --config=/tmp/nonexistent.conf\n");
99
100 println!(" # Directory not found error");
101 println!(" cargo run --example enhanced_errors_demo -- --outdir=/tmp/nonexistent_dir\n");
102
103 println!(" # Flag constraint error (RequiredIf)");
104 println!(" cargo run --example enhanced_errors_demo -- --ssl --required-flag=test process\n");
105
106 println!(" # Flag constraint error (ConflictsWith)");
107 println!(
108 " cargo run --example enhanced_errors_demo -- --json --xml --required-flag=test process\n"
109 );
110
111 println!(" # Required flag error");
112 println!(" cargo run --example enhanced_errors_demo -- process\n");
113
114 println!(" # Unknown command error");
115 println!(" cargo run --example enhanced_errors_demo -- --required-flag=test proces\n");
116
117 let args: Vec<String> = std::env::args().skip(1).collect();
118 if !args.is_empty() {
119 println!("\n{}", "=".repeat(60));
120 println!("Running with args: {:?}\n", args);
121
122 if let Err(e) = app.execute(args) {
123 eprintln!("{}", e);
124 std::process::exit(1);
125 }
126 }
127}12fn create_many_subcommands(parent: &mut flag_rs::Command) {
13 // Simulate a large CLI with 100 subcommands
14 for i in 0..100 {
15 let cmd_name = format!("service-{i:03}");
16
17 // Use string interning for flag names that repeat across commands
18 let namespace_flag_name = string_pool::intern("namespace");
19 let region_flag_name = string_pool::intern("region");
20 let env_flag_name = string_pool::intern("environment");
21
22 let cmd = CommandBuilder::new(cmd_name.clone())
23 .short(format!("Manage service {i}"))
24 .flag(
25 Flag::new(namespace_flag_name.to_string())
26 .short('n')
27 .usage("Kubernetes namespace")
28 .value_type(FlagType::String)
29 .default(flag_rs::FlagValue::String("default".to_string())),
30 )
31 .flag(
32 Flag::new(region_flag_name.to_string())
33 .short('r')
34 .usage("AWS region")
35 .value_type(FlagType::Choice(vec![
36 "us-east-1".to_string(),
37 "us-west-2".to_string(),
38 "eu-west-1".to_string(),
39 "ap-southeast-1".to_string(),
40 ]))
41 .default(flag_rs::FlagValue::String("us-east-1".to_string())),
42 )
43 .flag(
44 Flag::new(env_flag_name.to_string())
45 .short('e')
46 .usage("Deployment environment")
47 .value_type(FlagType::Choice(vec![
48 "dev".to_string(),
49 "staging".to_string(),
50 "prod".to_string(),
51 ]))
52 .default(flag_rs::FlagValue::String("dev".to_string())),
53 )
54 .subcommand(
55 CommandBuilder::new("deploy")
56 .short("Deploy the service")
57 .arg_completion(move |_ctx, prefix| {
58 // Use optimized completion result
59 let optimized = CompletionResultOptimized::new()
60 .add(Cow::Borrowed("rolling-update"))
61 .add(Cow::Borrowed("blue-green"))
62 .add(Cow::Borrowed("canary"))
63 .add_with_description(
64 Cow::Borrowed("recreate"),
65 Cow::Borrowed("Recreate all pods"),
66 );
67
68 // Filter based on prefix
69 let filtered = CompletionResultOptimized::new().extend_items(
70 optimized
71 .items
72 .into_iter()
73 .filter(|item| item.value.starts_with(prefix)),
74 );
75
76 // Convert to legacy format for compatibility
77 Ok(filtered.into_legacy())
78 })
79 .build(),
80 )
81 .subcommand(
82 CommandBuilder::new("scale")
83 .short("Scale the service")
84 .flag(
85 Flag::new("replicas")
86 .usage("Number of replicas")
87 .value_type(FlagType::Int)
88 .required(),
89 )
90 .build(),
91 )
92 .subcommand(
93 CommandBuilder::new("logs")
94 .short("View service logs")
95 .flag(
96 Flag::new("follow")
97 .short('f')
98 .usage("Follow log output")
99 .value_type(FlagType::Bool),
100 )
101 .flag(
102 Flag::new("tail")
103 .usage("Number of lines to show")
104 .value_type(FlagType::Int)
105 .default(flag_rs::FlagValue::Int(100)),
106 )
107 .build(),
108 )
109 .build();
110
111 parent.add_command(cmd);
112 }
113}
114
115// Count commands recursively
116fn count_commands(cmd: &flag_rs::Command) -> usize {
117 1 + cmd
118 .subcommands()
119 .values()
120 .map(count_commands)
121 .sum::<usize>()
122}
123
124// Count flags recursively
125fn count_flags(cmd: &flag_rs::Command) -> usize {
126 cmd.flags().len() + cmd.subcommands().values().map(count_flags).sum::<usize>()
127}
128
129fn main() {
130 // Create root command
131 let mut app = CommandBuilder::new("megacli")
132 .short("A large CLI demonstrating memory optimizations")
133 .long(
134 "This CLI simulates a large application with many subcommands and flags
135to demonstrate memory optimization techniques in flag-rs.
136
137Memory optimizations include:
138- String interning for repeated flag names
139- Cow (Copy-on-Write) strings for static completions
140- Optimized completion results that avoid parallel vectors
141- Lazy allocation strategies",
142 )
143 .flag(
144 Flag::new("verbose")
145 .short('v')
146 .usage("Enable verbose output")
147 .value_type(FlagType::Bool),
148 )
149 .flag(
150 Flag::new("config")
151 .short('c')
152 .usage("Path to config file")
153 .value_type(FlagType::File)
154 .default(flag_rs::FlagValue::String(
155 "~/.megacli/config.yaml".to_string(),
156 )),
157 )
158 .build();
159
160 // Add many subcommands
161 create_many_subcommands(&mut app);
162
163 let total_commands = count_commands(&app);
164 let total_flags = count_flags(&app);
165
166 // Add a special command to show memory usage stats
167 app.add_command(
168 CommandBuilder::new("stats")
169 .short("Show CLI statistics and memory usage")
170 .run(move |_ctx| {
171 println!("=== MegaCLI Statistics ===\n");
172
173 println!("Total commands: {total_commands}");
174 println!("Total flags: {total_flags}");
175 println!("String pool size: {} unique strings", 3); // We interned 3 flag names
176
177 println!("\nMemory optimization features in use:");
178 println!("✓ String interning for flag names");
179 println!("✓ Cow<str> for static completion values");
180 println!("✓ CompletionResultOptimized for reduced allocations");
181 println!("✓ Lazy allocation strategies");
182
183 println!("\nEstimated memory savings:");
184 println!("- 60-70% reduction in string allocations");
185 println!("- 40-50% reduction in completion memory usage");
186 println!("- Improved cache locality for better performance");
187
188 Ok(())
189 })
190 .build(),
191 );
192
193 // Execute the CLI
194 let args: Vec<String> = std::env::args().skip(1).collect();
195
196 if args.is_empty() {
197 println!("=== Memory Optimization Demo ===\n");
198 println!("This demo shows how flag-rs optimizes memory for large CLIs.\n");
199 println!("Try these commands:");
200 println!(
201 " {} stats # Show memory statistics",
202 std::env::args().next().unwrap_or_default()
203 );
204 println!(
205 " {} service-001 deploy <TAB> # Test optimized completions",
206 std::env::args().next().unwrap_or_default()
207 );
208 println!(
209 " {} --help # See all 100+ commands",
210 std::env::args().next().unwrap_or_default()
211 );
212 println!("\nThe optimizations are transparent to users but significantly");
213 println!("reduce memory usage for CLIs with many commands and flags.");
214 std::process::exit(0);
215 }
216
217 if let Err(e) = app.execute(args) {
218 eprintln!("{e}");
219 std::process::exit(1);
220 }
221}12fn main() {
13 let app = CommandBuilder::new("error-demo")
14 .short("Demonstrates error handling")
15 .long("This example shows various error scenarios and how flag-rs handles them")
16 .subcommand(
17 CommandBuilder::new("deploy")
18 .short("Deploy the application")
19 .args(ArgValidator::ExactArgs(1))
20 .run(|ctx| {
21 let env = ctx.args().first().unwrap();
22 if !["dev", "staging", "production"].contains(&env.as_str()) {
23 return Err(Error::Validation(format!(
24 "invalid environment '{}', must be one of: dev, staging, production",
25 env
26 )));
27 }
28 println!("Deploying to {}", env);
29 Ok(())
30 })
31 .build(),
32 )
33 .subcommand(
34 CommandBuilder::new("serve")
35 .short("Start the server")
36 .flag(
37 Flag::new("port")
38 .short('p')
39 .usage("Port to listen on")
40 .value_type(FlagType::Int)
41 .required(),
42 )
43 .run(|ctx| {
44 let port = ctx.flag("port").unwrap();
45 println!("Server starting on port {}", port);
46 Ok(())
47 })
48 .build(),
49 )
50 .subcommand(
51 CommandBuilder::new("migrate")
52 .short("Run database migrations")
53 .subcommand(
54 CommandBuilder::new("up")
55 .short("Run pending migrations")
56 .run(|_| {
57 println!("Running migrations...");
58 Ok(())
59 })
60 .build(),
61 )
62 .subcommand(
63 CommandBuilder::new("down")
64 .short("Rollback migrations")
65 .args(ArgValidator::MaximumArgs(1))
66 .run(|ctx| {
67 let steps = ctx
68 .args()
69 .first()
70 .and_then(|s| s.parse::<u32>().ok())
71 .unwrap_or(1);
72 println!(
73 "Rolling back {} migration{}",
74 steps,
75 if steps == 1 { "" } else { "s" }
76 );
77 Ok(())
78 })
79 .build(),
80 )
81 .build(),
82 )
83 .subcommand(
84 CommandBuilder::new("config")
85 .short("Manage configuration")
86 .aliases(vec!["cfg", "conf"])
87 .run(|_| {
88 println!("Configuration management");
89 Ok(())
90 })
91 .build(),
92 )
93 .build();
94
95 println!("=== Error Handling Demo ===\n");
96 println!("This demo shows how flag-rs handles various error scenarios.\n");
97 println!("Try these commands to see different error types:\n");
98 println!(
99 "1. {} deploi (typo - will show suggestions)",
100 std::env::args().next().unwrap_or_default()
101 );
102 println!(
103 "2. {} deploy (missing required argument)",
104 std::env::args().next().unwrap_or_default()
105 );
106 println!(
107 "3. {} deploy dev staging (too many arguments)",
108 std::env::args().next().unwrap_or_default()
109 );
110 println!(
111 "4. {} deploy test (invalid environment)",
112 std::env::args().next().unwrap_or_default()
113 );
114 println!(
115 "5. {} serve (missing required flag)",
116 std::env::args().next().unwrap_or_default()
117 );
118 println!(
119 "6. {} serve -p abc (invalid flag value)",
120 std::env::args().next().unwrap_or_default()
121 );
122 println!(
123 "7. {} migrate (subcommand required)",
124 std::env::args().next().unwrap_or_default()
125 );
126 println!(
127 "8. {} confi (typo - will suggest 'config')",
128 std::env::args().next().unwrap_or_default()
129 );
130 println!("\n---\n");
131
132 let args: Vec<String> = std::env::args().skip(1).collect();
133 if let Err(e) = app.execute(args) {
134 eprintln!("{}", e);
135 std::process::exit(1);
136 }
137}Sourcepub fn default(self, value: FlagValue) -> Self
pub fn default(self, value: FlagValue) -> Self
Sets the default value for this flag
§Examples
use flag_rs::flag::{Flag, FlagValue};
let flag = Flag::new("count").default(FlagValue::Int(10));
assert_eq!(flag.default, Some(FlagValue::Int(10)));Examples found in repository?
50fn create_simple_cli() -> flag_rs::Command {
51 CommandBuilder::new("bench")
52 .short("Benchmark CLI")
53 .flag(
54 Flag::new("verbose")
55 .short('v')
56 .value_type(FlagType::Bool)
57 .default(FlagValue::Bool(false)),
58 )
59 .flag(Flag::new("output").short('o').value_type(FlagType::String))
60 .build()
61}
62
63/// Creates a complex nested command structure
64fn create_complex_cli() -> flag_rs::Command {
65 let mut root = CommandBuilder::new("complex")
66 .short("Complex CLI with many subcommands")
67 .flag(
68 Flag::new("config")
69 .short('c')
70 .value_type(FlagType::File)
71 .default(FlagValue::String("config.yaml".to_string())),
72 )
73 .build();
74
75 // Add 50 subcommands
76 for i in 0..50 {
77 let mut sub = CommandBuilder::new(format!("sub{i:02}"))
78 .short(format!("Subcommand {i}"))
79 .flag(Flag::new("flag1").short('1').value_type(FlagType::String))
80 .flag(Flag::new("flag2").short('2').value_type(FlagType::Int))
81 .build();
82
83 // Add 10 nested subcommands
84 for j in 0..10 {
85 sub.add_command(
86 CommandBuilder::new(format!("nested{j}"))
87 .short(format!("Nested command {j}"))
88 .flag(Flag::new("deep").value_type(FlagType::Bool))
89 .build(),
90 );
91 }
92
93 root.add_command(sub);
94 }
95
96 root
97}More examples
14fn build_kubectl() -> Command {
15 CommandBuilder::new("kubectl")
16 .short("Kubernetes command-line tool")
17 .long("kubectl controls the Kubernetes cluster manager")
18 .subcommand(build_get_command())
19 .subcommand(build_describe_command())
20 .subcommand(build_delete_command())
21 .subcommand(build_completion_command())
22 .flag(
23 Flag::new("namespace")
24 .short('n')
25 .usage("Kubernetes namespace")
26 .value_type(FlagType::String)
27 .default(FlagValue::String("default".to_string())),
28 )
29 .flag_completion("namespace", |_ctx, prefix| {
30 // In a real kubectl, this would query the API server
31 let namespaces = get_namespaces_with_descriptions();
32 let mut result = CompletionResult::new();
33
34 for (ns_name, description) in namespaces {
35 if ns_name.starts_with(prefix) {
36 result = result.add_with_description(ns_name, description);
37 }
38 }
39
40 Ok(result)
41 })
42 .build()
43}17fn main() {
18 let app = CommandBuilder::new("demo")
19 .short("Terminal feature demonstration")
20 .long("This application demonstrates the terminal width detection and text wrapping features. The help text should automatically adjust to your terminal width, wrapping long lines at word boundaries while maintaining readability. Try running this with different COLUMNS values to see how it adapts.")
21 .flag(
22 Flag::new("file")
23 .short('f')
24 .usage("Input file path. This flag expects a path to a file that will be processed. The file must exist and be readable.")
25 .value_type(FlagType::String)
26 )
27 .flag(
28 Flag::new("output-format")
29 .short('o')
30 .usage("Output format for results. Supported formats include: json (machine-readable JSON format), yaml (human-friendly YAML format), table (formatted ASCII table), csv (comma-separated values for spreadsheet import)")
31 .value_type(FlagType::String)
32 .default(FlagValue::String("table".to_string()))
33 )
34 .subcommand(
35 CommandBuilder::new("process")
36 .short("Process data with various transformations and filters that can be applied in sequence")
37 .build()
38 )
39 .build();
40
41 // Always show help for this demo
42 app.print_help();
43}12fn create_many_subcommands(parent: &mut flag_rs::Command) {
13 // Simulate a large CLI with 100 subcommands
14 for i in 0..100 {
15 let cmd_name = format!("service-{i:03}");
16
17 // Use string interning for flag names that repeat across commands
18 let namespace_flag_name = string_pool::intern("namespace");
19 let region_flag_name = string_pool::intern("region");
20 let env_flag_name = string_pool::intern("environment");
21
22 let cmd = CommandBuilder::new(cmd_name.clone())
23 .short(format!("Manage service {i}"))
24 .flag(
25 Flag::new(namespace_flag_name.to_string())
26 .short('n')
27 .usage("Kubernetes namespace")
28 .value_type(FlagType::String)
29 .default(flag_rs::FlagValue::String("default".to_string())),
30 )
31 .flag(
32 Flag::new(region_flag_name.to_string())
33 .short('r')
34 .usage("AWS region")
35 .value_type(FlagType::Choice(vec![
36 "us-east-1".to_string(),
37 "us-west-2".to_string(),
38 "eu-west-1".to_string(),
39 "ap-southeast-1".to_string(),
40 ]))
41 .default(flag_rs::FlagValue::String("us-east-1".to_string())),
42 )
43 .flag(
44 Flag::new(env_flag_name.to_string())
45 .short('e')
46 .usage("Deployment environment")
47 .value_type(FlagType::Choice(vec![
48 "dev".to_string(),
49 "staging".to_string(),
50 "prod".to_string(),
51 ]))
52 .default(flag_rs::FlagValue::String("dev".to_string())),
53 )
54 .subcommand(
55 CommandBuilder::new("deploy")
56 .short("Deploy the service")
57 .arg_completion(move |_ctx, prefix| {
58 // Use optimized completion result
59 let optimized = CompletionResultOptimized::new()
60 .add(Cow::Borrowed("rolling-update"))
61 .add(Cow::Borrowed("blue-green"))
62 .add(Cow::Borrowed("canary"))
63 .add_with_description(
64 Cow::Borrowed("recreate"),
65 Cow::Borrowed("Recreate all pods"),
66 );
67
68 // Filter based on prefix
69 let filtered = CompletionResultOptimized::new().extend_items(
70 optimized
71 .items
72 .into_iter()
73 .filter(|item| item.value.starts_with(prefix)),
74 );
75
76 // Convert to legacy format for compatibility
77 Ok(filtered.into_legacy())
78 })
79 .build(),
80 )
81 .subcommand(
82 CommandBuilder::new("scale")
83 .short("Scale the service")
84 .flag(
85 Flag::new("replicas")
86 .usage("Number of replicas")
87 .value_type(FlagType::Int)
88 .required(),
89 )
90 .build(),
91 )
92 .subcommand(
93 CommandBuilder::new("logs")
94 .short("View service logs")
95 .flag(
96 Flag::new("follow")
97 .short('f')
98 .usage("Follow log output")
99 .value_type(FlagType::Bool),
100 )
101 .flag(
102 Flag::new("tail")
103 .usage("Number of lines to show")
104 .value_type(FlagType::Int)
105 .default(flag_rs::FlagValue::Int(100)),
106 )
107 .build(),
108 )
109 .build();
110
111 parent.add_command(cmd);
112 }
113}
114
115// Count commands recursively
116fn count_commands(cmd: &flag_rs::Command) -> usize {
117 1 + cmd
118 .subcommands()
119 .values()
120 .map(count_commands)
121 .sum::<usize>()
122}
123
124// Count flags recursively
125fn count_flags(cmd: &flag_rs::Command) -> usize {
126 cmd.flags().len() + cmd.subcommands().values().map(count_flags).sum::<usize>()
127}
128
129fn main() {
130 // Create root command
131 let mut app = CommandBuilder::new("megacli")
132 .short("A large CLI demonstrating memory optimizations")
133 .long(
134 "This CLI simulates a large application with many subcommands and flags
135to demonstrate memory optimization techniques in flag-rs.
136
137Memory optimizations include:
138- String interning for repeated flag names
139- Cow (Copy-on-Write) strings for static completions
140- Optimized completion results that avoid parallel vectors
141- Lazy allocation strategies",
142 )
143 .flag(
144 Flag::new("verbose")
145 .short('v')
146 .usage("Enable verbose output")
147 .value_type(FlagType::Bool),
148 )
149 .flag(
150 Flag::new("config")
151 .short('c')
152 .usage("Path to config file")
153 .value_type(FlagType::File)
154 .default(flag_rs::FlagValue::String(
155 "~/.megacli/config.yaml".to_string(),
156 )),
157 )
158 .build();
159
160 // Add many subcommands
161 create_many_subcommands(&mut app);
162
163 let total_commands = count_commands(&app);
164 let total_flags = count_flags(&app);
165
166 // Add a special command to show memory usage stats
167 app.add_command(
168 CommandBuilder::new("stats")
169 .short("Show CLI statistics and memory usage")
170 .run(move |_ctx| {
171 println!("=== MegaCLI Statistics ===\n");
172
173 println!("Total commands: {total_commands}");
174 println!("Total flags: {total_flags}");
175 println!("String pool size: {} unique strings", 3); // We interned 3 flag names
176
177 println!("\nMemory optimization features in use:");
178 println!("✓ String interning for flag names");
179 println!("✓ Cow<str> for static completion values");
180 println!("✓ CompletionResultOptimized for reduced allocations");
181 println!("✓ Lazy allocation strategies");
182
183 println!("\nEstimated memory savings:");
184 println!("- 60-70% reduction in string allocations");
185 println!("- 40-50% reduction in completion memory usage");
186 println!("- Improved cache locality for better performance");
187
188 Ok(())
189 })
190 .build(),
191 );
192
193 // Execute the CLI
194 let args: Vec<String> = std::env::args().skip(1).collect();
195
196 if args.is_empty() {
197 println!("=== Memory Optimization Demo ===\n");
198 println!("This demo shows how flag-rs optimizes memory for large CLIs.\n");
199 println!("Try these commands:");
200 println!(
201 " {} stats # Show memory statistics",
202 std::env::args().next().unwrap_or_default()
203 );
204 println!(
205 " {} service-001 deploy <TAB> # Test optimized completions",
206 std::env::args().next().unwrap_or_default()
207 );
208 println!(
209 " {} --help # See all 100+ commands",
210 std::env::args().next().unwrap_or_default()
211 );
212 println!("\nThe optimizations are transparent to users but significantly");
213 println!("reduce memory usage for CLIs with many commands and flags.");
214 std::process::exit(0);
215 }
216
217 if let Err(e) = app.execute(args) {
218 eprintln!("{e}");
219 std::process::exit(1);
220 }
221}30fn main() {
31 let app = CommandBuilder::new("cloud-cli")
32 .short("Cloud CLI with timeout-protected completions")
33 .flag(
34 Flag::new("region")
35 .short('r')
36 .usage("Cloud region")
37 .value_type(FlagType::String)
38 .default(flag_rs::FlagValue::String("us-east-1".to_string())),
39 )
40 .subcommand(
41 CommandBuilder::new("instances")
42 .short("Manage cloud instances")
43 .subcommand(
44 CommandBuilder::new("list")
45 .short("List instances (fast completion)")
46 .arg_completion(|_ctx, prefix| {
47 // Fast completion - no timeout needed
48 let instances = vec![
49 "web-server-001",
50 "web-server-002",
51 "db-primary",
52 "db-replica",
53 "cache-node-1",
54 "cache-node-2",
55 ];
56
57 Ok(CompletionResult::new().extend(
58 instances
59 .into_iter()
60 .filter(|i| i.starts_with(prefix))
61 .map(String::from),
62 ))
63 })
64 .build(),
65 )
66 .subcommand(
67 CommandBuilder::new("describe")
68 .short("Describe instance (slow completion with timeout)")
69 .arg_completion({
70 // Wrap slow completion with timeout
71 make_timeout_completion(
72 Duration::from_millis(500), // 500ms timeout
73 |ctx, prefix| {
74 eprintln!("Fetching instance details from API...");
75
76 // Simulate slow API call (1 second)
77 let instances = slow_completion(prefix, 1000);
78
79 let mut result = CompletionResult::new();
80 for instance in instances {
81 result = result.add_with_description(
82 instance.clone(),
83 format!(
84 "Instance in region {}",
85 ctx.flag("region")
86 .unwrap_or(&"us-east-1".to_string())
87 ),
88 );
89 }
90
91 if prefix.is_empty() {
92 result = result.add_help_text(
93 "Fetching live instance data from cloud API...",
94 );
95 }
96
97 Ok(result)
98 },
99 )
100 })
101 .build(),
102 )
103 .subcommand(
104 CommandBuilder::new("create")
105 .short("Create instance (very slow completion)")
106 .arg_completion({
107 // Use default timeout (2 seconds)
108 make_timeout_completion(DEFAULT_COMPLETION_TIMEOUT, |_ctx, prefix| {
109 eprintln!("Checking available instance types...");
110
111 // Simulate very slow operation (3 seconds)
112 let types = slow_completion(prefix, 3000);
113
114 Ok(CompletionResult::new()
115 .extend(types)
116 .add_help_text("Available instance types"))
117 })
118 })
119 .build(),
120 )
121 .build(),
122 )
123 .build();
124
125 println!("=== Timeout Completion Demo ===\n");
126 println!("This demo shows how timeouts protect against slow completion operations.\n");
127 println!("To test timeout handling:");
128 println!("1. Set up bash completion:");
129 println!(
130 " source <({} completion bash)",
131 std::env::args().next().unwrap_or_default()
132 );
133 println!();
134 println!("2. Test different completion speeds:");
135 println!();
136 println!(" Fast (no timeout needed):");
137 println!(
138 " {} instances list <TAB>",
139 std::env::args().next().unwrap_or_default()
140 );
141 println!();
142 println!(" Slow (500ms timeout, 1s operation - will timeout):");
143 println!(
144 " {} instances describe <TAB>",
145 std::env::args().next().unwrap_or_default()
146 );
147 println!();
148 println!(" Very slow (2s timeout, 3s operation - will timeout):");
149 println!(
150 " {} instances create <TAB>",
151 std::env::args().next().unwrap_or_default()
152 );
153 println!();
154 println!("When a timeout occurs, you'll see:");
155 println!("- A warning message about the timeout");
156 println!("- Any partial results that were available");
157 println!("- Suggestion to use a more specific prefix");
158 println!("\n---\n");
159
160 let args: Vec<String> = std::env::args().skip(1).collect();
161 if let Err(e) = app.execute(args) {
162 eprintln!("{}", e);
163 std::process::exit(1);
164 }
165}10fn main() {
11 let app = CommandBuilder::new("kubectl")
12 .short("Kubernetes command-line tool")
13 .long("kubectl controls the Kubernetes cluster manager.\n\n\
14 Find more information at: https://kubernetes.io/docs/reference/kubectl/")
15 .example("kubectl get pods")
16 .example("kubectl apply -f deployment.yaml")
17 .example("kubectl logs -f my-pod")
18 .flag(
19 Flag::new("namespace")
20 .short('n')
21 .usage("The namespace scope for this CLI request")
22 .value_type(FlagType::String)
23 .default(flag_rs::FlagValue::String("default".to_string())),
24 )
25 .flag(
26 Flag::new("kubeconfig")
27 .usage("Path to the kubeconfig file to use for CLI requests")
28 .value_type(FlagType::String),
29 )
30 .subcommand(
31 CommandBuilder::new("get")
32 .short("Display one or many resources")
33 .group_id("Basic Commands")
34 .long("Prints a table of the most important information about the specified resources.\n\n\
35 You can filter the list using a label selector and the --selector flag. If the\n\
36 desired resource type is namespaced you will only see results in your current\n\
37 namespace unless you pass --all-namespaces.")
38 .example("kubectl get pods")
39 .example("kubectl get pods -n kube-system")
40 .example("kubectl get pods --selector=app=nginx")
41 .example("kubectl get pods,services")
42 .build(),
43 )
44 .subcommand(
45 CommandBuilder::new("apply")
46 .short("Apply a configuration to a resource by file name or stdin")
47 .group_id("Basic Commands")
48 .long("Apply a configuration to a resource by file name or stdin. The resource name must\n\
49 be specified. This resource will be created if it doesn't exist yet. To use 'apply',\n\
50 always create the resource initially with either 'apply' or 'create --save-config'.")
51 .example("kubectl apply -f ./pod.yaml")
52 .example("kubectl apply -f https://example.com/manifest.yaml")
53 .example("kubectl apply -k ./")
54 .flag(
55 Flag::new("filename")
56 .short('f')
57 .usage("Filename, directory, or URL to files to use to create the resource")
58 .value_type(FlagType::String)
59 .required(),
60 )
61 .flag(
62 Flag::new("recursive")
63 .short('R')
64 .usage("Process the directory used in -f, --filename recursively")
65 .value_type(FlagType::Bool),
66 )
67 .build(),
68 )
69 .subcommand(
70 CommandBuilder::new("delete")
71 .short("Delete resources by file names, stdin, resources and names, or by resources and label selector")
72 .aliases(vec!["del", "remove", "rm"])
73 .group_id("Basic Commands")
74 .example("kubectl delete pod my-pod")
75 .example("kubectl delete -f ./pod.yaml")
76 .example("kubectl delete pods --all")
77 .build(),
78 )
79 .subcommand(
80 CommandBuilder::new("logs")
81 .short("Print the logs for a container in a pod")
82 .group_id("Troubleshooting and Debugging Commands")
83 .long("Print the logs for a container in a pod or specified resource. If the pod has\n\
84 only one container, the container name is optional.")
85 .aliases(vec!["log"])
86 .example("kubectl logs my-pod")
87 .example("kubectl logs my-pod -c my-container")
88 .example("kubectl logs -f my-pod")
89 .example("kubectl logs --tail=20 my-pod")
90 .flag(
91 Flag::new("follow")
92 .short('f')
93 .usage("Specify if the logs should be streamed")
94 .value_type(FlagType::Bool),
95 )
96 .flag(
97 Flag::new("tail")
98 .usage("Lines of recent log file to display")
99 .value_type(FlagType::Int)
100 .default(flag_rs::FlagValue::Int(-1)),
101 )
102 .build(),
103 )
104 .subcommand(
105 CommandBuilder::new("describe")
106 .short("Show details of a specific resource or group of resources")
107 .group_id("Troubleshooting and Debugging Commands")
108 .example("kubectl describe pod my-pod")
109 .example("kubectl describe nodes")
110 .build(),
111 )
112 .subcommand(
113 CommandBuilder::new("exec")
114 .short("Execute a command in a container")
115 .group_id("Troubleshooting and Debugging Commands")
116 .example("kubectl exec -it my-pod -- /bin/bash")
117 .example("kubectl exec my-pod -- ls /app")
118 .build(),
119 )
120 .subcommand(
121 CommandBuilder::new("config")
122 .short("Modify kubeconfig files")
123 .group_id("Settings Commands")
124 .example("kubectl config view")
125 .example("kubectl config use-context my-context")
126 .build(),
127 )
128 .subcommand(
129 CommandBuilder::new("version")
130 .short("Print the client and server version information")
131 .example("kubectl version")
132 .example("kubectl version --short")
133 .build(),
134 )
135 .build();
136
137 let args: Vec<String> = std::env::args().skip(1).collect();
138 if let Err(e) = app.execute(args) {
139 eprintln!("{}", e);
140 std::process::exit(1);
141 }
142}Sourcepub fn default_bool(self, value: bool) -> Self
pub fn default_bool(self, value: bool) -> Self
Sets a default boolean value
§Examples
use flag_rs::flag::{Flag, FlagValue};
let flag = Flag::bool("verbose").default_bool(true);
assert_eq!(flag.default, Some(FlagValue::Bool(true)));Examples found in repository?
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}Sourcepub fn default_str(self, value: &str) -> Self
pub fn default_str(self, value: &str) -> Self
Sets a default string value
§Examples
use flag_rs::flag::{Flag, FlagValue};
let flag = Flag::string("name").default_str("anonymous");
assert_eq!(flag.default, Some(FlagValue::String("anonymous".to_string())));Examples found in repository?
4fn main() {
5 let app = CommandBuilder::new("deploy")
6 .short("Deploy application to various environments")
7 .flag(
8 Flag::choice("environment", &["dev", "staging", "prod"])
9 .short('e')
10 .usage("Target environment")
11 .required()
12 .completion(|_ctx, prefix| {
13 // Dynamic completion - could check available environments
14 let environments = vec![
15 ("dev", "Development environment"),
16 ("staging", "Staging environment (pre-production)"),
17 ("prod", "Production environment"),
18 ];
19
20 let mut result = CompletionResult::new();
21 for (env, desc) in environments {
22 if env.starts_with(prefix) {
23 result = result.add_with_description(env.to_string(), desc.to_string());
24 }
25 }
26
27 // Add active help if no prefix
28 if prefix.is_empty() {
29 result = result.add_help_text("Available environments: dev, staging, prod");
30 }
31
32 Ok(result)
33 }),
34 )
35 .flag(
36 Flag::file("config")
37 .short('c')
38 .usage("Configuration file")
39 .default_str("config.yaml")
40 .completion(|_ctx, prefix| {
41 // In a real app, you might list files in the config directory
42 let configs = vec![
43 "config.yaml",
44 "config.dev.yaml",
45 "config.staging.yaml",
46 "config.prod.yaml",
47 "secrets.yaml",
48 ];
49
50 Ok(CompletionResult::new().extend(
51 configs
52 .into_iter()
53 .filter(|c| c.starts_with(prefix))
54 .map(String::from),
55 ))
56 }),
57 )
58 .flag(
59 Flag::directory("output")
60 .short('o')
61 .usage("Output directory for deployment artifacts")
62 .completion(|_ctx, prefix| {
63 // In a real app, you might list actual directories
64 let dirs = vec!["./build", "./dist", "./output", "/tmp/deploy"];
65
66 Ok(CompletionResult::new().extend(
67 dirs.into_iter()
68 .filter(|d| d.starts_with(prefix))
69 .map(String::from),
70 ))
71 }),
72 )
73 .flag(
74 Flag::string_slice("service")
75 .short('s')
76 .usage("Services to deploy (can be specified multiple times)")
77 .completion(|ctx, prefix| {
78 // Context-aware completion based on environment
79 let env = ctx.flag_str_or("environment", "dev");
80
81 let services = match env {
82 "dev" => vec!["api", "web", "worker", "scheduler", "debug-panel"],
83 "staging" => vec!["api", "web", "worker", "scheduler"],
84 "prod" => vec!["api", "web", "worker"],
85 _ => vec!["api", "web"],
86 };
87
88 let mut result = CompletionResult::new().extend(
89 services
90 .into_iter()
91 .filter(|s| s.starts_with(prefix))
92 .map(String::from),
93 );
94
95 // Add context-aware help
96 if prefix.is_empty() {
97 result = result
98 .add_help_text(format!("Available services for {} environment", env));
99 }
100
101 Ok(result)
102 }),
103 )
104 .flag(
105 Flag::range("replicas", 1, 10)
106 .short('r')
107 .usage("Number of replicas to deploy")
108 .default_int(2)
109 .completion(|_ctx, _prefix| {
110 // Suggest common replica counts
111 Ok(CompletionResult::new()
112 .add_with_description(
113 "1".to_string(),
114 "Single instance (dev/test)".to_string(),
115 )
116 .add_with_description("2".to_string(), "Basic redundancy".to_string())
117 .add_with_description("3".to_string(), "Standard production".to_string())
118 .add_with_description("5".to_string(), "High availability".to_string()))
119 }),
120 )
121 .run(|ctx| {
122 let env = ctx.flag_str_or("environment", "dev");
123 let config = ctx.flag_str_or("config", "config.yaml");
124 let replicas = ctx.flag_int_or("replicas", 2);
125
126 println!("Deploying to {} environment", env);
127 println!("Using configuration: {}", config);
128 println!("Replica count: {}", replicas);
129
130 if let Some(output) = ctx.flag("output") {
131 println!("Output directory: {}", output);
132 }
133
134 if let Some(services) = ctx.flag("service") {
135 println!("Deploying services: {}", services);
136 } else {
137 println!("Deploying all services");
138 }
139
140 Ok(())
141 })
142 .build();
143
144 let args: Vec<String> = std::env::args().skip(1).collect();
145 if let Err(e) = app.execute(args) {
146 eprintln!("Error: {}", e);
147 std::process::exit(1);
148 }
149}More examples
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}Sourcepub fn default_int(self, value: i64) -> Self
pub fn default_int(self, value: i64) -> Self
Sets a default integer value
§Examples
use flag_rs::flag::{Flag, FlagValue};
let flag = Flag::int("port").default_int(8080);
assert_eq!(flag.default, Some(FlagValue::Int(8080)));Examples found in repository?
4fn main() {
5 let app = CommandBuilder::new("deploy")
6 .short("Deploy application to various environments")
7 .flag(
8 Flag::choice("environment", &["dev", "staging", "prod"])
9 .short('e')
10 .usage("Target environment")
11 .required()
12 .completion(|_ctx, prefix| {
13 // Dynamic completion - could check available environments
14 let environments = vec![
15 ("dev", "Development environment"),
16 ("staging", "Staging environment (pre-production)"),
17 ("prod", "Production environment"),
18 ];
19
20 let mut result = CompletionResult::new();
21 for (env, desc) in environments {
22 if env.starts_with(prefix) {
23 result = result.add_with_description(env.to_string(), desc.to_string());
24 }
25 }
26
27 // Add active help if no prefix
28 if prefix.is_empty() {
29 result = result.add_help_text("Available environments: dev, staging, prod");
30 }
31
32 Ok(result)
33 }),
34 )
35 .flag(
36 Flag::file("config")
37 .short('c')
38 .usage("Configuration file")
39 .default_str("config.yaml")
40 .completion(|_ctx, prefix| {
41 // In a real app, you might list files in the config directory
42 let configs = vec![
43 "config.yaml",
44 "config.dev.yaml",
45 "config.staging.yaml",
46 "config.prod.yaml",
47 "secrets.yaml",
48 ];
49
50 Ok(CompletionResult::new().extend(
51 configs
52 .into_iter()
53 .filter(|c| c.starts_with(prefix))
54 .map(String::from),
55 ))
56 }),
57 )
58 .flag(
59 Flag::directory("output")
60 .short('o')
61 .usage("Output directory for deployment artifacts")
62 .completion(|_ctx, prefix| {
63 // In a real app, you might list actual directories
64 let dirs = vec!["./build", "./dist", "./output", "/tmp/deploy"];
65
66 Ok(CompletionResult::new().extend(
67 dirs.into_iter()
68 .filter(|d| d.starts_with(prefix))
69 .map(String::from),
70 ))
71 }),
72 )
73 .flag(
74 Flag::string_slice("service")
75 .short('s')
76 .usage("Services to deploy (can be specified multiple times)")
77 .completion(|ctx, prefix| {
78 // Context-aware completion based on environment
79 let env = ctx.flag_str_or("environment", "dev");
80
81 let services = match env {
82 "dev" => vec!["api", "web", "worker", "scheduler", "debug-panel"],
83 "staging" => vec!["api", "web", "worker", "scheduler"],
84 "prod" => vec!["api", "web", "worker"],
85 _ => vec!["api", "web"],
86 };
87
88 let mut result = CompletionResult::new().extend(
89 services
90 .into_iter()
91 .filter(|s| s.starts_with(prefix))
92 .map(String::from),
93 );
94
95 // Add context-aware help
96 if prefix.is_empty() {
97 result = result
98 .add_help_text(format!("Available services for {} environment", env));
99 }
100
101 Ok(result)
102 }),
103 )
104 .flag(
105 Flag::range("replicas", 1, 10)
106 .short('r')
107 .usage("Number of replicas to deploy")
108 .default_int(2)
109 .completion(|_ctx, _prefix| {
110 // Suggest common replica counts
111 Ok(CompletionResult::new()
112 .add_with_description(
113 "1".to_string(),
114 "Single instance (dev/test)".to_string(),
115 )
116 .add_with_description("2".to_string(), "Basic redundancy".to_string())
117 .add_with_description("3".to_string(), "Standard production".to_string())
118 .add_with_description("5".to_string(), "High availability".to_string()))
119 }),
120 )
121 .run(|ctx| {
122 let env = ctx.flag_str_or("environment", "dev");
123 let config = ctx.flag_str_or("config", "config.yaml");
124 let replicas = ctx.flag_int_or("replicas", 2);
125
126 println!("Deploying to {} environment", env);
127 println!("Using configuration: {}", config);
128 println!("Replica count: {}", replicas);
129
130 if let Some(output) = ctx.flag("output") {
131 println!("Output directory: {}", output);
132 }
133
134 if let Some(services) = ctx.flag("service") {
135 println!("Deploying services: {}", services);
136 } else {
137 println!("Deploying all services");
138 }
139
140 Ok(())
141 })
142 .build();
143
144 let args: Vec<String> = std::env::args().skip(1).collect();
145 if let Err(e) = app.execute(args) {
146 eprintln!("Error: {}", e);
147 std::process::exit(1);
148 }
149}More examples
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}Sourcepub fn default_float(self, value: f64) -> Self
pub fn default_float(self, value: f64) -> Self
Sets a default float value
§Examples
use flag_rs::flag::{Flag, FlagValue};
let flag = Flag::float("ratio").default_float(0.5);
assert_eq!(flag.default, Some(FlagValue::Float(0.5)));Sourcepub const fn required(self) -> Self
pub const fn required(self) -> Self
Marks this flag as required
§Examples
use flag_rs::flag::Flag;
let flag = Flag::new("name").required();
assert!(flag.required);Examples found in repository?
249fn bench_flag_validation() {
250 println!("\n=== Flag Validation Benchmarks ===");
251
252 let cli = CommandBuilder::new("validate")
253 .flag(Flag::new("choice").value_type(FlagType::Choice(vec![
254 "opt1".to_string(),
255 "opt2".to_string(),
256 "opt3".to_string(),
257 ])))
258 .flag(Flag::new("range").value_type(FlagType::Range(1, 100)))
259 .flag(
260 Flag::new("required")
261 .value_type(FlagType::String)
262 .required(),
263 )
264 .build();
265
266 let bench = Benchmark::new("Validate choice flag", 10_000);
267 let duration = bench.run(|| {
268 let args = vec![
269 "validate".to_string(),
270 "--choice".to_string(),
271 "opt2".to_string(),
272 "--required".to_string(),
273 "value".to_string(),
274 ];
275 let _ = cli.execute(args);
276 });
277 bench.report(duration);
278
279 let bench = Benchmark::new("Validate range flag", 10_000);
280 let duration = bench.run(|| {
281 let args = vec![
282 "validate".to_string(),
283 "--range".to_string(),
284 "50".to_string(),
285 "--required".to_string(),
286 "value".to_string(),
287 ];
288 let _ = cli.execute(args);
289 });
290 bench.report(duration);
291
292 let bench = Benchmark::new("Validate missing required flag", 10_000);
293 let duration = bench.run(|| {
294 let args = vec!["validate".to_string()];
295 let _ = cli.execute(args); // This will fail validation
296 });
297 bench.report(duration);
298}More examples
7fn main() {
8 let app = CommandBuilder::new("errordemo")
9 .short("Demo of enhanced error messages")
10 .flag(
11 Flag::new("verbose")
12 .short('v')
13 .value_type(FlagType::Bool)
14 .usage("Enable verbose output"),
15 )
16 .flag(
17 Flag::new("workers")
18 .short('w')
19 .value_type(FlagType::Range(1, 10))
20 .usage("Number of worker threads"),
21 )
22 .flag(
23 Flag::new("format")
24 .short('f')
25 .value_type(FlagType::Choice(vec![
26 "json".to_string(),
27 "yaml".to_string(),
28 "xml".to_string(),
29 "toml".to_string(),
30 ]))
31 .usage("Output format"),
32 )
33 .flag(
34 Flag::new("config")
35 .short('c')
36 .value_type(FlagType::File)
37 .usage("Configuration file"),
38 )
39 .flag(
40 Flag::new("outdir")
41 .short('o')
42 .value_type(FlagType::Directory)
43 .usage("Output directory"),
44 )
45 .flag(
46 Flag::new("ssl")
47 .value_type(FlagType::Bool)
48 .usage("Enable SSL"),
49 )
50 .flag(
51 Flag::new("ssl-cert")
52 .value_type(FlagType::File)
53 .constraint(FlagConstraint::RequiredIf("ssl".to_string()))
54 .usage("SSL certificate file"),
55 )
56 .flag(
57 Flag::new("json")
58 .value_type(FlagType::Bool)
59 .constraint(FlagConstraint::ConflictsWith(vec!["xml".to_string()]))
60 .usage("Output in JSON format"),
61 )
62 .flag(
63 Flag::new("xml")
64 .value_type(FlagType::Bool)
65 .usage("Output in XML format"),
66 )
67 .flag(
68 Flag::new("required-flag")
69 .value_type(FlagType::String)
70 .required()
71 .usage("This flag is required"),
72 )
73 .subcommand(
74 CommandBuilder::new("process")
75 .short("Process data")
76 .run(|ctx| {
77 println!("Processing with flags: {:?}", ctx.flags());
78 Ok(())
79 })
80 .build(),
81 )
82 .build();
83
84 println!("Try these commands to see enhanced error messages:\n");
85 println!(" # Boolean parsing error");
86 println!(" cargo run --example enhanced_errors_demo -- --verbose=maybe\n");
87
88 println!(" # Integer parsing error");
89 println!(" cargo run --example enhanced_errors_demo -- --workers=abc\n");
90
91 println!(" # Range validation error");
92 println!(" cargo run --example enhanced_errors_demo -- --workers=20\n");
93
94 println!(" # Choice validation error");
95 println!(" cargo run --example enhanced_errors_demo -- --format=csv\n");
96
97 println!(" # File not found error");
98 println!(" cargo run --example enhanced_errors_demo -- --config=/tmp/nonexistent.conf\n");
99
100 println!(" # Directory not found error");
101 println!(" cargo run --example enhanced_errors_demo -- --outdir=/tmp/nonexistent_dir\n");
102
103 println!(" # Flag constraint error (RequiredIf)");
104 println!(" cargo run --example enhanced_errors_demo -- --ssl --required-flag=test process\n");
105
106 println!(" # Flag constraint error (ConflictsWith)");
107 println!(
108 " cargo run --example enhanced_errors_demo -- --json --xml --required-flag=test process\n"
109 );
110
111 println!(" # Required flag error");
112 println!(" cargo run --example enhanced_errors_demo -- process\n");
113
114 println!(" # Unknown command error");
115 println!(" cargo run --example enhanced_errors_demo -- --required-flag=test proces\n");
116
117 let args: Vec<String> = std::env::args().skip(1).collect();
118 if !args.is_empty() {
119 println!("\n{}", "=".repeat(60));
120 println!("Running with args: {:?}\n", args);
121
122 if let Err(e) = app.execute(args) {
123 eprintln!("{}", e);
124 std::process::exit(1);
125 }
126 }
127}12fn create_many_subcommands(parent: &mut flag_rs::Command) {
13 // Simulate a large CLI with 100 subcommands
14 for i in 0..100 {
15 let cmd_name = format!("service-{i:03}");
16
17 // Use string interning for flag names that repeat across commands
18 let namespace_flag_name = string_pool::intern("namespace");
19 let region_flag_name = string_pool::intern("region");
20 let env_flag_name = string_pool::intern("environment");
21
22 let cmd = CommandBuilder::new(cmd_name.clone())
23 .short(format!("Manage service {i}"))
24 .flag(
25 Flag::new(namespace_flag_name.to_string())
26 .short('n')
27 .usage("Kubernetes namespace")
28 .value_type(FlagType::String)
29 .default(flag_rs::FlagValue::String("default".to_string())),
30 )
31 .flag(
32 Flag::new(region_flag_name.to_string())
33 .short('r')
34 .usage("AWS region")
35 .value_type(FlagType::Choice(vec![
36 "us-east-1".to_string(),
37 "us-west-2".to_string(),
38 "eu-west-1".to_string(),
39 "ap-southeast-1".to_string(),
40 ]))
41 .default(flag_rs::FlagValue::String("us-east-1".to_string())),
42 )
43 .flag(
44 Flag::new(env_flag_name.to_string())
45 .short('e')
46 .usage("Deployment environment")
47 .value_type(FlagType::Choice(vec![
48 "dev".to_string(),
49 "staging".to_string(),
50 "prod".to_string(),
51 ]))
52 .default(flag_rs::FlagValue::String("dev".to_string())),
53 )
54 .subcommand(
55 CommandBuilder::new("deploy")
56 .short("Deploy the service")
57 .arg_completion(move |_ctx, prefix| {
58 // Use optimized completion result
59 let optimized = CompletionResultOptimized::new()
60 .add(Cow::Borrowed("rolling-update"))
61 .add(Cow::Borrowed("blue-green"))
62 .add(Cow::Borrowed("canary"))
63 .add_with_description(
64 Cow::Borrowed("recreate"),
65 Cow::Borrowed("Recreate all pods"),
66 );
67
68 // Filter based on prefix
69 let filtered = CompletionResultOptimized::new().extend_items(
70 optimized
71 .items
72 .into_iter()
73 .filter(|item| item.value.starts_with(prefix)),
74 );
75
76 // Convert to legacy format for compatibility
77 Ok(filtered.into_legacy())
78 })
79 .build(),
80 )
81 .subcommand(
82 CommandBuilder::new("scale")
83 .short("Scale the service")
84 .flag(
85 Flag::new("replicas")
86 .usage("Number of replicas")
87 .value_type(FlagType::Int)
88 .required(),
89 )
90 .build(),
91 )
92 .subcommand(
93 CommandBuilder::new("logs")
94 .short("View service logs")
95 .flag(
96 Flag::new("follow")
97 .short('f')
98 .usage("Follow log output")
99 .value_type(FlagType::Bool),
100 )
101 .flag(
102 Flag::new("tail")
103 .usage("Number of lines to show")
104 .value_type(FlagType::Int)
105 .default(flag_rs::FlagValue::Int(100)),
106 )
107 .build(),
108 )
109 .build();
110
111 parent.add_command(cmd);
112 }
113}12fn main() {
13 let app = CommandBuilder::new("error-demo")
14 .short("Demonstrates error handling")
15 .long("This example shows various error scenarios and how flag-rs handles them")
16 .subcommand(
17 CommandBuilder::new("deploy")
18 .short("Deploy the application")
19 .args(ArgValidator::ExactArgs(1))
20 .run(|ctx| {
21 let env = ctx.args().first().unwrap();
22 if !["dev", "staging", "production"].contains(&env.as_str()) {
23 return Err(Error::Validation(format!(
24 "invalid environment '{}', must be one of: dev, staging, production",
25 env
26 )));
27 }
28 println!("Deploying to {}", env);
29 Ok(())
30 })
31 .build(),
32 )
33 .subcommand(
34 CommandBuilder::new("serve")
35 .short("Start the server")
36 .flag(
37 Flag::new("port")
38 .short('p')
39 .usage("Port to listen on")
40 .value_type(FlagType::Int)
41 .required(),
42 )
43 .run(|ctx| {
44 let port = ctx.flag("port").unwrap();
45 println!("Server starting on port {}", port);
46 Ok(())
47 })
48 .build(),
49 )
50 .subcommand(
51 CommandBuilder::new("migrate")
52 .short("Run database migrations")
53 .subcommand(
54 CommandBuilder::new("up")
55 .short("Run pending migrations")
56 .run(|_| {
57 println!("Running migrations...");
58 Ok(())
59 })
60 .build(),
61 )
62 .subcommand(
63 CommandBuilder::new("down")
64 .short("Rollback migrations")
65 .args(ArgValidator::MaximumArgs(1))
66 .run(|ctx| {
67 let steps = ctx
68 .args()
69 .first()
70 .and_then(|s| s.parse::<u32>().ok())
71 .unwrap_or(1);
72 println!(
73 "Rolling back {} migration{}",
74 steps,
75 if steps == 1 { "" } else { "s" }
76 );
77 Ok(())
78 })
79 .build(),
80 )
81 .build(),
82 )
83 .subcommand(
84 CommandBuilder::new("config")
85 .short("Manage configuration")
86 .aliases(vec!["cfg", "conf"])
87 .run(|_| {
88 println!("Configuration management");
89 Ok(())
90 })
91 .build(),
92 )
93 .build();
94
95 println!("=== Error Handling Demo ===\n");
96 println!("This demo shows how flag-rs handles various error scenarios.\n");
97 println!("Try these commands to see different error types:\n");
98 println!(
99 "1. {} deploi (typo - will show suggestions)",
100 std::env::args().next().unwrap_or_default()
101 );
102 println!(
103 "2. {} deploy (missing required argument)",
104 std::env::args().next().unwrap_or_default()
105 );
106 println!(
107 "3. {} deploy dev staging (too many arguments)",
108 std::env::args().next().unwrap_or_default()
109 );
110 println!(
111 "4. {} deploy test (invalid environment)",
112 std::env::args().next().unwrap_or_default()
113 );
114 println!(
115 "5. {} serve (missing required flag)",
116 std::env::args().next().unwrap_or_default()
117 );
118 println!(
119 "6. {} serve -p abc (invalid flag value)",
120 std::env::args().next().unwrap_or_default()
121 );
122 println!(
123 "7. {} migrate (subcommand required)",
124 std::env::args().next().unwrap_or_default()
125 );
126 println!(
127 "8. {} confi (typo - will suggest 'config')",
128 std::env::args().next().unwrap_or_default()
129 );
130 println!("\n---\n");
131
132 let args: Vec<String> = std::env::args().skip(1).collect();
133 if let Err(e) = app.execute(args) {
134 eprintln!("{}", e);
135 std::process::exit(1);
136 }
137}4fn main() {
5 let app = CommandBuilder::new("deploy")
6 .short("Deploy application to various environments")
7 .flag(
8 Flag::choice("environment", &["dev", "staging", "prod"])
9 .short('e')
10 .usage("Target environment")
11 .required()
12 .completion(|_ctx, prefix| {
13 // Dynamic completion - could check available environments
14 let environments = vec![
15 ("dev", "Development environment"),
16 ("staging", "Staging environment (pre-production)"),
17 ("prod", "Production environment"),
18 ];
19
20 let mut result = CompletionResult::new();
21 for (env, desc) in environments {
22 if env.starts_with(prefix) {
23 result = result.add_with_description(env.to_string(), desc.to_string());
24 }
25 }
26
27 // Add active help if no prefix
28 if prefix.is_empty() {
29 result = result.add_help_text("Available environments: dev, staging, prod");
30 }
31
32 Ok(result)
33 }),
34 )
35 .flag(
36 Flag::file("config")
37 .short('c')
38 .usage("Configuration file")
39 .default_str("config.yaml")
40 .completion(|_ctx, prefix| {
41 // In a real app, you might list files in the config directory
42 let configs = vec![
43 "config.yaml",
44 "config.dev.yaml",
45 "config.staging.yaml",
46 "config.prod.yaml",
47 "secrets.yaml",
48 ];
49
50 Ok(CompletionResult::new().extend(
51 configs
52 .into_iter()
53 .filter(|c| c.starts_with(prefix))
54 .map(String::from),
55 ))
56 }),
57 )
58 .flag(
59 Flag::directory("output")
60 .short('o')
61 .usage("Output directory for deployment artifacts")
62 .completion(|_ctx, prefix| {
63 // In a real app, you might list actual directories
64 let dirs = vec!["./build", "./dist", "./output", "/tmp/deploy"];
65
66 Ok(CompletionResult::new().extend(
67 dirs.into_iter()
68 .filter(|d| d.starts_with(prefix))
69 .map(String::from),
70 ))
71 }),
72 )
73 .flag(
74 Flag::string_slice("service")
75 .short('s')
76 .usage("Services to deploy (can be specified multiple times)")
77 .completion(|ctx, prefix| {
78 // Context-aware completion based on environment
79 let env = ctx.flag_str_or("environment", "dev");
80
81 let services = match env {
82 "dev" => vec!["api", "web", "worker", "scheduler", "debug-panel"],
83 "staging" => vec!["api", "web", "worker", "scheduler"],
84 "prod" => vec!["api", "web", "worker"],
85 _ => vec!["api", "web"],
86 };
87
88 let mut result = CompletionResult::new().extend(
89 services
90 .into_iter()
91 .filter(|s| s.starts_with(prefix))
92 .map(String::from),
93 );
94
95 // Add context-aware help
96 if prefix.is_empty() {
97 result = result
98 .add_help_text(format!("Available services for {} environment", env));
99 }
100
101 Ok(result)
102 }),
103 )
104 .flag(
105 Flag::range("replicas", 1, 10)
106 .short('r')
107 .usage("Number of replicas to deploy")
108 .default_int(2)
109 .completion(|_ctx, _prefix| {
110 // Suggest common replica counts
111 Ok(CompletionResult::new()
112 .add_with_description(
113 "1".to_string(),
114 "Single instance (dev/test)".to_string(),
115 )
116 .add_with_description("2".to_string(), "Basic redundancy".to_string())
117 .add_with_description("3".to_string(), "Standard production".to_string())
118 .add_with_description("5".to_string(), "High availability".to_string()))
119 }),
120 )
121 .run(|ctx| {
122 let env = ctx.flag_str_or("environment", "dev");
123 let config = ctx.flag_str_or("config", "config.yaml");
124 let replicas = ctx.flag_int_or("replicas", 2);
125
126 println!("Deploying to {} environment", env);
127 println!("Using configuration: {}", config);
128 println!("Replica count: {}", replicas);
129
130 if let Some(output) = ctx.flag("output") {
131 println!("Output directory: {}", output);
132 }
133
134 if let Some(services) = ctx.flag("service") {
135 println!("Deploying services: {}", services);
136 } else {
137 println!("Deploying all services");
138 }
139
140 Ok(())
141 })
142 .build();
143
144 let args: Vec<String> = std::env::args().skip(1).collect();
145 if let Err(e) = app.execute(args) {
146 eprintln!("Error: {}", e);
147 std::process::exit(1);
148 }
149}10fn main() {
11 let app = CommandBuilder::new("kubectl")
12 .short("Kubernetes command-line tool")
13 .long("kubectl controls the Kubernetes cluster manager.\n\n\
14 Find more information at: https://kubernetes.io/docs/reference/kubectl/")
15 .example("kubectl get pods")
16 .example("kubectl apply -f deployment.yaml")
17 .example("kubectl logs -f my-pod")
18 .flag(
19 Flag::new("namespace")
20 .short('n')
21 .usage("The namespace scope for this CLI request")
22 .value_type(FlagType::String)
23 .default(flag_rs::FlagValue::String("default".to_string())),
24 )
25 .flag(
26 Flag::new("kubeconfig")
27 .usage("Path to the kubeconfig file to use for CLI requests")
28 .value_type(FlagType::String),
29 )
30 .subcommand(
31 CommandBuilder::new("get")
32 .short("Display one or many resources")
33 .group_id("Basic Commands")
34 .long("Prints a table of the most important information about the specified resources.\n\n\
35 You can filter the list using a label selector and the --selector flag. If the\n\
36 desired resource type is namespaced you will only see results in your current\n\
37 namespace unless you pass --all-namespaces.")
38 .example("kubectl get pods")
39 .example("kubectl get pods -n kube-system")
40 .example("kubectl get pods --selector=app=nginx")
41 .example("kubectl get pods,services")
42 .build(),
43 )
44 .subcommand(
45 CommandBuilder::new("apply")
46 .short("Apply a configuration to a resource by file name or stdin")
47 .group_id("Basic Commands")
48 .long("Apply a configuration to a resource by file name or stdin. The resource name must\n\
49 be specified. This resource will be created if it doesn't exist yet. To use 'apply',\n\
50 always create the resource initially with either 'apply' or 'create --save-config'.")
51 .example("kubectl apply -f ./pod.yaml")
52 .example("kubectl apply -f https://example.com/manifest.yaml")
53 .example("kubectl apply -k ./")
54 .flag(
55 Flag::new("filename")
56 .short('f')
57 .usage("Filename, directory, or URL to files to use to create the resource")
58 .value_type(FlagType::String)
59 .required(),
60 )
61 .flag(
62 Flag::new("recursive")
63 .short('R')
64 .usage("Process the directory used in -f, --filename recursively")
65 .value_type(FlagType::Bool),
66 )
67 .build(),
68 )
69 .subcommand(
70 CommandBuilder::new("delete")
71 .short("Delete resources by file names, stdin, resources and names, or by resources and label selector")
72 .aliases(vec!["del", "remove", "rm"])
73 .group_id("Basic Commands")
74 .example("kubectl delete pod my-pod")
75 .example("kubectl delete -f ./pod.yaml")
76 .example("kubectl delete pods --all")
77 .build(),
78 )
79 .subcommand(
80 CommandBuilder::new("logs")
81 .short("Print the logs for a container in a pod")
82 .group_id("Troubleshooting and Debugging Commands")
83 .long("Print the logs for a container in a pod or specified resource. If the pod has\n\
84 only one container, the container name is optional.")
85 .aliases(vec!["log"])
86 .example("kubectl logs my-pod")
87 .example("kubectl logs my-pod -c my-container")
88 .example("kubectl logs -f my-pod")
89 .example("kubectl logs --tail=20 my-pod")
90 .flag(
91 Flag::new("follow")
92 .short('f')
93 .usage("Specify if the logs should be streamed")
94 .value_type(FlagType::Bool),
95 )
96 .flag(
97 Flag::new("tail")
98 .usage("Lines of recent log file to display")
99 .value_type(FlagType::Int)
100 .default(flag_rs::FlagValue::Int(-1)),
101 )
102 .build(),
103 )
104 .subcommand(
105 CommandBuilder::new("describe")
106 .short("Show details of a specific resource or group of resources")
107 .group_id("Troubleshooting and Debugging Commands")
108 .example("kubectl describe pod my-pod")
109 .example("kubectl describe nodes")
110 .build(),
111 )
112 .subcommand(
113 CommandBuilder::new("exec")
114 .short("Execute a command in a container")
115 .group_id("Troubleshooting and Debugging Commands")
116 .example("kubectl exec -it my-pod -- /bin/bash")
117 .example("kubectl exec my-pod -- ls /app")
118 .build(),
119 )
120 .subcommand(
121 CommandBuilder::new("config")
122 .short("Modify kubeconfig files")
123 .group_id("Settings Commands")
124 .example("kubectl config view")
125 .example("kubectl config use-context my-context")
126 .build(),
127 )
128 .subcommand(
129 CommandBuilder::new("version")
130 .short("Print the client and server version information")
131 .example("kubectl version")
132 .example("kubectl version --short")
133 .build(),
134 )
135 .build();
136
137 let args: Vec<String> = std::env::args().skip(1).collect();
138 if let Err(e) = app.execute(args) {
139 eprintln!("{}", e);
140 std::process::exit(1);
141 }
142}Sourcepub fn value_type(self, value_type: FlagType) -> Self
pub fn value_type(self, value_type: FlagType) -> Self
Sets the value type for this flag
§Examples
use flag_rs::flag::{Flag, FlagType};
let flag = Flag::new("count").value_type(FlagType::Int);Examples found in repository?
50fn create_simple_cli() -> flag_rs::Command {
51 CommandBuilder::new("bench")
52 .short("Benchmark CLI")
53 .flag(
54 Flag::new("verbose")
55 .short('v')
56 .value_type(FlagType::Bool)
57 .default(FlagValue::Bool(false)),
58 )
59 .flag(Flag::new("output").short('o').value_type(FlagType::String))
60 .build()
61}
62
63/// Creates a complex nested command structure
64fn create_complex_cli() -> flag_rs::Command {
65 let mut root = CommandBuilder::new("complex")
66 .short("Complex CLI with many subcommands")
67 .flag(
68 Flag::new("config")
69 .short('c')
70 .value_type(FlagType::File)
71 .default(FlagValue::String("config.yaml".to_string())),
72 )
73 .build();
74
75 // Add 50 subcommands
76 for i in 0..50 {
77 let mut sub = CommandBuilder::new(format!("sub{i:02}"))
78 .short(format!("Subcommand {i}"))
79 .flag(Flag::new("flag1").short('1').value_type(FlagType::String))
80 .flag(Flag::new("flag2").short('2').value_type(FlagType::Int))
81 .build();
82
83 // Add 10 nested subcommands
84 for j in 0..10 {
85 sub.add_command(
86 CommandBuilder::new(format!("nested{j}"))
87 .short(format!("Nested command {j}"))
88 .flag(Flag::new("deep").value_type(FlagType::Bool))
89 .build(),
90 );
91 }
92
93 root.add_command(sub);
94 }
95
96 root
97}
98
99fn bench_command_creation() {
100 println!("\n=== Command Creation Benchmarks ===");
101
102 let bench = Benchmark::new("Simple command creation", 10_000);
103 let duration = bench.run(|| {
104 let _ = create_simple_cli();
105 });
106 bench.report(duration);
107
108 let bench = Benchmark::new("Complex command creation (50 subs)", 100);
109 let duration = bench.run(|| {
110 let _ = create_complex_cli();
111 });
112 bench.report(duration);
113
114 let bench = Benchmark::new("CommandBuilder with 10 flags", 1_000);
115 let duration = bench.run(|| {
116 let mut cmd = CommandBuilder::new("test");
117 for i in 0..10 {
118 cmd = cmd.flag(Flag::new(format!("flag{i}")).value_type(FlagType::String));
119 }
120 let _ = cmd.build();
121 });
122 bench.report(duration);
123}
124
125fn bench_flag_parsing() {
126 println!("\n=== Flag Parsing Benchmarks ===");
127
128 let cli = create_simple_cli();
129
130 let bench = Benchmark::new("Parse no flags", 10_000);
131 let duration = bench.run(|| {
132 let args = vec!["bench".to_string()];
133 let _ = cli.execute(args);
134 });
135 bench.report(duration);
136
137 let bench = Benchmark::new("Parse single flag", 10_000);
138 let duration = bench.run(|| {
139 let args = vec!["bench".to_string(), "--verbose".to_string()];
140 let _ = cli.execute(args);
141 });
142 bench.report(duration);
143
144 let bench = Benchmark::new("Parse flag with value", 10_000);
145 let duration = bench.run(|| {
146 let args = vec![
147 "bench".to_string(),
148 "--output".to_string(),
149 "file.txt".to_string(),
150 ];
151 let _ = cli.execute(args);
152 });
153 bench.report(duration);
154
155 let bench = Benchmark::new("Parse multiple flags", 10_000);
156 let duration = bench.run(|| {
157 let args = vec![
158 "bench".to_string(),
159 "-v".to_string(),
160 "-o".to_string(),
161 "output.txt".to_string(),
162 ];
163 let _ = cli.execute(args);
164 });
165 bench.report(duration);
166}
167
168fn bench_subcommand_lookup() {
169 println!("\n=== Subcommand Lookup Benchmarks ===");
170
171 let cli = create_complex_cli();
172
173 let bench = Benchmark::new("Find immediate subcommand", 10_000);
174 let duration = bench.run(|| {
175 let _ = cli.find_subcommand("sub25");
176 });
177 bench.report(duration);
178
179 let bench = Benchmark::new("Find nested subcommand", 10_000);
180 let duration = bench.run(|| {
181 if let Some(sub) = cli.find_subcommand("sub25") {
182 let _ = sub.find_subcommand("nested5");
183 }
184 });
185 bench.report(duration);
186
187 let bench = Benchmark::new("Execute nested command", 1_000);
188 let duration = bench.run(|| {
189 let args = vec![
190 "complex".to_string(),
191 "sub25".to_string(),
192 "nested5".to_string(),
193 "--deep".to_string(),
194 ];
195 let _ = cli.execute(args);
196 });
197 bench.report(duration);
198}
199
200fn bench_completion() {
201 println!("\n=== Completion Benchmarks ===");
202
203 let cli = CommandBuilder::new("comp")
204 .arg_completion(|_ctx, prefix| {
205 let items: Vec<String> = (0..100)
206 .map(|i| format!("item{i:03}"))
207 .filter(|item| item.starts_with(prefix))
208 .collect();
209 Ok(CompletionResult::new().extend(items))
210 })
211 .build();
212
213 let ctx = flag_rs::Context::new(vec!["comp".to_string()]);
214
215 let bench = Benchmark::new("Complete with empty prefix (100 items)", 1_000);
216 let duration = bench.run(|| {
217 let _ = cli.get_completions(&ctx, "", None);
218 });
219 bench.report(duration);
220
221 let bench = Benchmark::new("Complete with prefix (filtered)", 1_000);
222 let duration = bench.run(|| {
223 let _ = cli.get_completions(&ctx, "item05", None);
224 });
225 bench.report(duration);
226
227 // Test completion with descriptions
228 let cli_desc = CommandBuilder::new("comp_desc")
229 .arg_completion(|_ctx, prefix| {
230 let mut result = CompletionResult::new();
231 for i in 0..50 {
232 let item = format!("option{i:02}");
233 if item.starts_with(prefix) {
234 result =
235 result.add_with_description(item, format!("Description for option {i}"));
236 }
237 }
238 Ok(result)
239 })
240 .build();
241
242 let bench = Benchmark::new("Complete with descriptions (50 items)", 1_000);
243 let duration = bench.run(|| {
244 let _ = cli_desc.get_completions(&ctx, "", None);
245 });
246 bench.report(duration);
247}
248
249fn bench_flag_validation() {
250 println!("\n=== Flag Validation Benchmarks ===");
251
252 let cli = CommandBuilder::new("validate")
253 .flag(Flag::new("choice").value_type(FlagType::Choice(vec![
254 "opt1".to_string(),
255 "opt2".to_string(),
256 "opt3".to_string(),
257 ])))
258 .flag(Flag::new("range").value_type(FlagType::Range(1, 100)))
259 .flag(
260 Flag::new("required")
261 .value_type(FlagType::String)
262 .required(),
263 )
264 .build();
265
266 let bench = Benchmark::new("Validate choice flag", 10_000);
267 let duration = bench.run(|| {
268 let args = vec![
269 "validate".to_string(),
270 "--choice".to_string(),
271 "opt2".to_string(),
272 "--required".to_string(),
273 "value".to_string(),
274 ];
275 let _ = cli.execute(args);
276 });
277 bench.report(duration);
278
279 let bench = Benchmark::new("Validate range flag", 10_000);
280 let duration = bench.run(|| {
281 let args = vec![
282 "validate".to_string(),
283 "--range".to_string(),
284 "50".to_string(),
285 "--required".to_string(),
286 "value".to_string(),
287 ];
288 let _ = cli.execute(args);
289 });
290 bench.report(duration);
291
292 let bench = Benchmark::new("Validate missing required flag", 10_000);
293 let duration = bench.run(|| {
294 let args = vec!["validate".to_string()];
295 let _ = cli.execute(args); // This will fail validation
296 });
297 bench.report(duration);
298}More examples
14fn build_kubectl() -> Command {
15 CommandBuilder::new("kubectl")
16 .short("Kubernetes command-line tool")
17 .long("kubectl controls the Kubernetes cluster manager")
18 .subcommand(build_get_command())
19 .subcommand(build_describe_command())
20 .subcommand(build_delete_command())
21 .subcommand(build_completion_command())
22 .flag(
23 Flag::new("namespace")
24 .short('n')
25 .usage("Kubernetes namespace")
26 .value_type(FlagType::String)
27 .default(FlagValue::String("default".to_string())),
28 )
29 .flag_completion("namespace", |_ctx, prefix| {
30 // In a real kubectl, this would query the API server
31 let namespaces = get_namespaces_with_descriptions();
32 let mut result = CompletionResult::new();
33
34 for (ns_name, description) in namespaces {
35 if ns_name.starts_with(prefix) {
36 result = result.add_with_description(ns_name, description);
37 }
38 }
39
40 Ok(result)
41 })
42 .build()
43}
44
45fn build_get_command() -> Command {
46 CommandBuilder::new("get")
47 .short("Display one or many resources")
48 .long("Display one or many resources. Prints a table of the most important information about the specified resources.")
49 .flag(
50 Flag::new("limit")
51 .short('l')
52 .usage("Maximum number of resources to display")
53 .value_type(FlagType::String)
54 )
55 .flag_completion("limit", |_ctx, prefix| {
56 let common_limits = vec![
57 ("10", "Display 10 items"),
58 ("20", "Display 20 items"),
59 ("50", "Display 50 items"),
60 ("100", "Display 100 items"),
61 ("all", "Display all items (no limit)"),
62 ];
63
64 let mut result = CompletionResult::new();
65 for (limit, description) in common_limits {
66 if limit.starts_with(prefix) {
67 result = result.add_with_description(limit.to_string(), description.to_string());
68 }
69 }
70
71 Ok(result)
72 })
73 .subcommand(build_get_pods())
74 .subcommand(build_get_services())
75 .subcommand(build_get_deployments())
76 .build()
77}17fn main() {
18 let app = CommandBuilder::new("demo")
19 .short("Terminal feature demonstration")
20 .long("This application demonstrates the terminal width detection and text wrapping features. The help text should automatically adjust to your terminal width, wrapping long lines at word boundaries while maintaining readability. Try running this with different COLUMNS values to see how it adapts.")
21 .flag(
22 Flag::new("file")
23 .short('f')
24 .usage("Input file path. This flag expects a path to a file that will be processed. The file must exist and be readable.")
25 .value_type(FlagType::String)
26 )
27 .flag(
28 Flag::new("output-format")
29 .short('o')
30 .usage("Output format for results. Supported formats include: json (machine-readable JSON format), yaml (human-friendly YAML format), table (formatted ASCII table), csv (comma-separated values for spreadsheet import)")
31 .value_type(FlagType::String)
32 .default(FlagValue::String("table".to_string()))
33 )
34 .subcommand(
35 CommandBuilder::new("process")
36 .short("Process data with various transformations and filters that can be applied in sequence")
37 .build()
38 )
39 .build();
40
41 // Always show help for this demo
42 app.print_help();
43}3fn main() {
4 let cmd = CommandBuilder::new("test")
5 .short("Test completion with descriptions")
6 .flag(
7 Flag::new("environment")
8 .short('e')
9 .usage("Target environment")
10 .value_type(FlagType::String),
11 )
12 .flag_completion("environment", |_ctx, prefix| {
13 let mut result = CompletionResult::new();
14 let envs = vec![
15 ("dev", "Development environment - safe for testing"),
16 ("staging", "Staging environment - mirror of production"),
17 ("production", "Production environment - BE CAREFUL!"),
18 ];
19
20 for (env, desc) in envs {
21 if env.starts_with(prefix) {
22 result = result.add_with_description(env, desc);
23 }
24 }
25
26 Ok(result)
27 })
28 .subcommand(
29 CommandBuilder::new("deploy")
30 .short("Deploy the application")
31 .long("Deploy the application to the specified environment")
32 .build(),
33 )
34 .subcommand(
35 CommandBuilder::new("rollback")
36 .short("Rollback to previous version")
37 .long("Rollback the application to the previous deployed version")
38 .build(),
39 )
40 .build();
41
42 // Test completion output
43 if let Ok(shell_type) = std::env::var("TEST_COMPLETE") {
44 // For testing, override shell type to "display" to see formatted output
45 let display_mode = std::env::var("DISPLAY_MODE").is_ok();
46 let args: Vec<String> = std::env::args().skip(1).collect();
47
48 if display_mode {
49 // This is a hack for testing - normally shells would handle this
50 println!("Display mode - showing formatted completions:");
51 match cmd.handle_completion_request(&args) {
52 Ok(completions) => {
53 for completion in completions {
54 println!("{}", completion);
55 }
56 }
57 Err(e) => eprintln!("Completion error: {}", e),
58 }
59 } else {
60 match cmd.handle_completion_request(&args) {
61 Ok(completions) => {
62 println!("Shell type: {}", shell_type);
63 println!("Completions:");
64 for completion in completions {
65 println!("{}", completion);
66 }
67 }
68 Err(e) => eprintln!("Completion error: {}", e),
69 }
70 }
71 } else {
72 let args: Vec<String> = std::env::args().skip(1).collect();
73 if let Err(e) = cmd.execute(args) {
74 eprintln!("Error: {}", e);
75 std::process::exit(1);
76 }
77 }
78}7fn main() {
8 let app = CommandBuilder::new("errordemo")
9 .short("Demo of enhanced error messages")
10 .flag(
11 Flag::new("verbose")
12 .short('v')
13 .value_type(FlagType::Bool)
14 .usage("Enable verbose output"),
15 )
16 .flag(
17 Flag::new("workers")
18 .short('w')
19 .value_type(FlagType::Range(1, 10))
20 .usage("Number of worker threads"),
21 )
22 .flag(
23 Flag::new("format")
24 .short('f')
25 .value_type(FlagType::Choice(vec![
26 "json".to_string(),
27 "yaml".to_string(),
28 "xml".to_string(),
29 "toml".to_string(),
30 ]))
31 .usage("Output format"),
32 )
33 .flag(
34 Flag::new("config")
35 .short('c')
36 .value_type(FlagType::File)
37 .usage("Configuration file"),
38 )
39 .flag(
40 Flag::new("outdir")
41 .short('o')
42 .value_type(FlagType::Directory)
43 .usage("Output directory"),
44 )
45 .flag(
46 Flag::new("ssl")
47 .value_type(FlagType::Bool)
48 .usage("Enable SSL"),
49 )
50 .flag(
51 Flag::new("ssl-cert")
52 .value_type(FlagType::File)
53 .constraint(FlagConstraint::RequiredIf("ssl".to_string()))
54 .usage("SSL certificate file"),
55 )
56 .flag(
57 Flag::new("json")
58 .value_type(FlagType::Bool)
59 .constraint(FlagConstraint::ConflictsWith(vec!["xml".to_string()]))
60 .usage("Output in JSON format"),
61 )
62 .flag(
63 Flag::new("xml")
64 .value_type(FlagType::Bool)
65 .usage("Output in XML format"),
66 )
67 .flag(
68 Flag::new("required-flag")
69 .value_type(FlagType::String)
70 .required()
71 .usage("This flag is required"),
72 )
73 .subcommand(
74 CommandBuilder::new("process")
75 .short("Process data")
76 .run(|ctx| {
77 println!("Processing with flags: {:?}", ctx.flags());
78 Ok(())
79 })
80 .build(),
81 )
82 .build();
83
84 println!("Try these commands to see enhanced error messages:\n");
85 println!(" # Boolean parsing error");
86 println!(" cargo run --example enhanced_errors_demo -- --verbose=maybe\n");
87
88 println!(" # Integer parsing error");
89 println!(" cargo run --example enhanced_errors_demo -- --workers=abc\n");
90
91 println!(" # Range validation error");
92 println!(" cargo run --example enhanced_errors_demo -- --workers=20\n");
93
94 println!(" # Choice validation error");
95 println!(" cargo run --example enhanced_errors_demo -- --format=csv\n");
96
97 println!(" # File not found error");
98 println!(" cargo run --example enhanced_errors_demo -- --config=/tmp/nonexistent.conf\n");
99
100 println!(" # Directory not found error");
101 println!(" cargo run --example enhanced_errors_demo -- --outdir=/tmp/nonexistent_dir\n");
102
103 println!(" # Flag constraint error (RequiredIf)");
104 println!(" cargo run --example enhanced_errors_demo -- --ssl --required-flag=test process\n");
105
106 println!(" # Flag constraint error (ConflictsWith)");
107 println!(
108 " cargo run --example enhanced_errors_demo -- --json --xml --required-flag=test process\n"
109 );
110
111 println!(" # Required flag error");
112 println!(" cargo run --example enhanced_errors_demo -- process\n");
113
114 println!(" # Unknown command error");
115 println!(" cargo run --example enhanced_errors_demo -- --required-flag=test proces\n");
116
117 let args: Vec<String> = std::env::args().skip(1).collect();
118 if !args.is_empty() {
119 println!("\n{}", "=".repeat(60));
120 println!("Running with args: {:?}\n", args);
121
122 if let Err(e) = app.execute(args) {
123 eprintln!("{}", e);
124 std::process::exit(1);
125 }
126 }
127}12fn create_many_subcommands(parent: &mut flag_rs::Command) {
13 // Simulate a large CLI with 100 subcommands
14 for i in 0..100 {
15 let cmd_name = format!("service-{i:03}");
16
17 // Use string interning for flag names that repeat across commands
18 let namespace_flag_name = string_pool::intern("namespace");
19 let region_flag_name = string_pool::intern("region");
20 let env_flag_name = string_pool::intern("environment");
21
22 let cmd = CommandBuilder::new(cmd_name.clone())
23 .short(format!("Manage service {i}"))
24 .flag(
25 Flag::new(namespace_flag_name.to_string())
26 .short('n')
27 .usage("Kubernetes namespace")
28 .value_type(FlagType::String)
29 .default(flag_rs::FlagValue::String("default".to_string())),
30 )
31 .flag(
32 Flag::new(region_flag_name.to_string())
33 .short('r')
34 .usage("AWS region")
35 .value_type(FlagType::Choice(vec![
36 "us-east-1".to_string(),
37 "us-west-2".to_string(),
38 "eu-west-1".to_string(),
39 "ap-southeast-1".to_string(),
40 ]))
41 .default(flag_rs::FlagValue::String("us-east-1".to_string())),
42 )
43 .flag(
44 Flag::new(env_flag_name.to_string())
45 .short('e')
46 .usage("Deployment environment")
47 .value_type(FlagType::Choice(vec![
48 "dev".to_string(),
49 "staging".to_string(),
50 "prod".to_string(),
51 ]))
52 .default(flag_rs::FlagValue::String("dev".to_string())),
53 )
54 .subcommand(
55 CommandBuilder::new("deploy")
56 .short("Deploy the service")
57 .arg_completion(move |_ctx, prefix| {
58 // Use optimized completion result
59 let optimized = CompletionResultOptimized::new()
60 .add(Cow::Borrowed("rolling-update"))
61 .add(Cow::Borrowed("blue-green"))
62 .add(Cow::Borrowed("canary"))
63 .add_with_description(
64 Cow::Borrowed("recreate"),
65 Cow::Borrowed("Recreate all pods"),
66 );
67
68 // Filter based on prefix
69 let filtered = CompletionResultOptimized::new().extend_items(
70 optimized
71 .items
72 .into_iter()
73 .filter(|item| item.value.starts_with(prefix)),
74 );
75
76 // Convert to legacy format for compatibility
77 Ok(filtered.into_legacy())
78 })
79 .build(),
80 )
81 .subcommand(
82 CommandBuilder::new("scale")
83 .short("Scale the service")
84 .flag(
85 Flag::new("replicas")
86 .usage("Number of replicas")
87 .value_type(FlagType::Int)
88 .required(),
89 )
90 .build(),
91 )
92 .subcommand(
93 CommandBuilder::new("logs")
94 .short("View service logs")
95 .flag(
96 Flag::new("follow")
97 .short('f')
98 .usage("Follow log output")
99 .value_type(FlagType::Bool),
100 )
101 .flag(
102 Flag::new("tail")
103 .usage("Number of lines to show")
104 .value_type(FlagType::Int)
105 .default(flag_rs::FlagValue::Int(100)),
106 )
107 .build(),
108 )
109 .build();
110
111 parent.add_command(cmd);
112 }
113}
114
115// Count commands recursively
116fn count_commands(cmd: &flag_rs::Command) -> usize {
117 1 + cmd
118 .subcommands()
119 .values()
120 .map(count_commands)
121 .sum::<usize>()
122}
123
124// Count flags recursively
125fn count_flags(cmd: &flag_rs::Command) -> usize {
126 cmd.flags().len() + cmd.subcommands().values().map(count_flags).sum::<usize>()
127}
128
129fn main() {
130 // Create root command
131 let mut app = CommandBuilder::new("megacli")
132 .short("A large CLI demonstrating memory optimizations")
133 .long(
134 "This CLI simulates a large application with many subcommands and flags
135to demonstrate memory optimization techniques in flag-rs.
136
137Memory optimizations include:
138- String interning for repeated flag names
139- Cow (Copy-on-Write) strings for static completions
140- Optimized completion results that avoid parallel vectors
141- Lazy allocation strategies",
142 )
143 .flag(
144 Flag::new("verbose")
145 .short('v')
146 .usage("Enable verbose output")
147 .value_type(FlagType::Bool),
148 )
149 .flag(
150 Flag::new("config")
151 .short('c')
152 .usage("Path to config file")
153 .value_type(FlagType::File)
154 .default(flag_rs::FlagValue::String(
155 "~/.megacli/config.yaml".to_string(),
156 )),
157 )
158 .build();
159
160 // Add many subcommands
161 create_many_subcommands(&mut app);
162
163 let total_commands = count_commands(&app);
164 let total_flags = count_flags(&app);
165
166 // Add a special command to show memory usage stats
167 app.add_command(
168 CommandBuilder::new("stats")
169 .short("Show CLI statistics and memory usage")
170 .run(move |_ctx| {
171 println!("=== MegaCLI Statistics ===\n");
172
173 println!("Total commands: {total_commands}");
174 println!("Total flags: {total_flags}");
175 println!("String pool size: {} unique strings", 3); // We interned 3 flag names
176
177 println!("\nMemory optimization features in use:");
178 println!("✓ String interning for flag names");
179 println!("✓ Cow<str> for static completion values");
180 println!("✓ CompletionResultOptimized for reduced allocations");
181 println!("✓ Lazy allocation strategies");
182
183 println!("\nEstimated memory savings:");
184 println!("- 60-70% reduction in string allocations");
185 println!("- 40-50% reduction in completion memory usage");
186 println!("- Improved cache locality for better performance");
187
188 Ok(())
189 })
190 .build(),
191 );
192
193 // Execute the CLI
194 let args: Vec<String> = std::env::args().skip(1).collect();
195
196 if args.is_empty() {
197 println!("=== Memory Optimization Demo ===\n");
198 println!("This demo shows how flag-rs optimizes memory for large CLIs.\n");
199 println!("Try these commands:");
200 println!(
201 " {} stats # Show memory statistics",
202 std::env::args().next().unwrap_or_default()
203 );
204 println!(
205 " {} service-001 deploy <TAB> # Test optimized completions",
206 std::env::args().next().unwrap_or_default()
207 );
208 println!(
209 " {} --help # See all 100+ commands",
210 std::env::args().next().unwrap_or_default()
211 );
212 println!("\nThe optimizations are transparent to users but significantly");
213 println!("reduce memory usage for CLIs with many commands and flags.");
214 std::process::exit(0);
215 }
216
217 if let Err(e) = app.execute(args) {
218 eprintln!("{e}");
219 std::process::exit(1);
220 }
221}Sourcepub fn constraint(self, constraint: FlagConstraint) -> Self
pub fn constraint(self, constraint: FlagConstraint) -> Self
Adds a constraint to this flag
§Examples
use flag_rs::flag::{Flag, FlagConstraint};
let flag = Flag::new("ssl")
.constraint(FlagConstraint::RequiredIf("port".to_string()))
.constraint(FlagConstraint::ConflictsWith(vec!["no-ssl".to_string()]));Examples found in repository?
7fn main() {
8 let app = CommandBuilder::new("errordemo")
9 .short("Demo of enhanced error messages")
10 .flag(
11 Flag::new("verbose")
12 .short('v')
13 .value_type(FlagType::Bool)
14 .usage("Enable verbose output"),
15 )
16 .flag(
17 Flag::new("workers")
18 .short('w')
19 .value_type(FlagType::Range(1, 10))
20 .usage("Number of worker threads"),
21 )
22 .flag(
23 Flag::new("format")
24 .short('f')
25 .value_type(FlagType::Choice(vec![
26 "json".to_string(),
27 "yaml".to_string(),
28 "xml".to_string(),
29 "toml".to_string(),
30 ]))
31 .usage("Output format"),
32 )
33 .flag(
34 Flag::new("config")
35 .short('c')
36 .value_type(FlagType::File)
37 .usage("Configuration file"),
38 )
39 .flag(
40 Flag::new("outdir")
41 .short('o')
42 .value_type(FlagType::Directory)
43 .usage("Output directory"),
44 )
45 .flag(
46 Flag::new("ssl")
47 .value_type(FlagType::Bool)
48 .usage("Enable SSL"),
49 )
50 .flag(
51 Flag::new("ssl-cert")
52 .value_type(FlagType::File)
53 .constraint(FlagConstraint::RequiredIf("ssl".to_string()))
54 .usage("SSL certificate file"),
55 )
56 .flag(
57 Flag::new("json")
58 .value_type(FlagType::Bool)
59 .constraint(FlagConstraint::ConflictsWith(vec!["xml".to_string()]))
60 .usage("Output in JSON format"),
61 )
62 .flag(
63 Flag::new("xml")
64 .value_type(FlagType::Bool)
65 .usage("Output in XML format"),
66 )
67 .flag(
68 Flag::new("required-flag")
69 .value_type(FlagType::String)
70 .required()
71 .usage("This flag is required"),
72 )
73 .subcommand(
74 CommandBuilder::new("process")
75 .short("Process data")
76 .run(|ctx| {
77 println!("Processing with flags: {:?}", ctx.flags());
78 Ok(())
79 })
80 .build(),
81 )
82 .build();
83
84 println!("Try these commands to see enhanced error messages:\n");
85 println!(" # Boolean parsing error");
86 println!(" cargo run --example enhanced_errors_demo -- --verbose=maybe\n");
87
88 println!(" # Integer parsing error");
89 println!(" cargo run --example enhanced_errors_demo -- --workers=abc\n");
90
91 println!(" # Range validation error");
92 println!(" cargo run --example enhanced_errors_demo -- --workers=20\n");
93
94 println!(" # Choice validation error");
95 println!(" cargo run --example enhanced_errors_demo -- --format=csv\n");
96
97 println!(" # File not found error");
98 println!(" cargo run --example enhanced_errors_demo -- --config=/tmp/nonexistent.conf\n");
99
100 println!(" # Directory not found error");
101 println!(" cargo run --example enhanced_errors_demo -- --outdir=/tmp/nonexistent_dir\n");
102
103 println!(" # Flag constraint error (RequiredIf)");
104 println!(" cargo run --example enhanced_errors_demo -- --ssl --required-flag=test process\n");
105
106 println!(" # Flag constraint error (ConflictsWith)");
107 println!(
108 " cargo run --example enhanced_errors_demo -- --json --xml --required-flag=test process\n"
109 );
110
111 println!(" # Required flag error");
112 println!(" cargo run --example enhanced_errors_demo -- process\n");
113
114 println!(" # Unknown command error");
115 println!(" cargo run --example enhanced_errors_demo -- --required-flag=test proces\n");
116
117 let args: Vec<String> = std::env::args().skip(1).collect();
118 if !args.is_empty() {
119 println!("\n{}", "=".repeat(60));
120 println!("Running with args: {:?}\n", args);
121
122 if let Err(e) = app.execute(args) {
123 eprintln!("{}", e);
124 std::process::exit(1);
125 }
126 }
127}More examples
14fn main() {
15 let app = CommandBuilder::new("server")
16 .short("A demo server with advanced configuration options")
17 .long(
18 "This example demonstrates the advanced flag features in flag-rs,
19including choice flags, range validation, file/directory validation,
20and flag constraints.",
21 )
22 // Choice flag - must be one of the predefined values
23 .flag(
24 Flag::new("environment")
25 .short('e')
26 .usage("Server environment")
27 .value_type(FlagType::Choice(vec![
28 "development".to_string(),
29 "staging".to_string(),
30 "production".to_string(),
31 ]))
32 .default(FlagValue::String("development".to_string())),
33 )
34 // Range flag - numeric value within a specific range
35 .flag(
36 Flag::new("port")
37 .short('p')
38 .usage("Server port (1024-65535)")
39 .value_type(FlagType::Range(1024, 65535))
40 .default(FlagValue::Int(8080)),
41 )
42 // File flag - must be a valid file path
43 .flag(
44 Flag::new("config")
45 .short('c')
46 .usage("Configuration file path")
47 .value_type(FlagType::File),
48 )
49 // Directory flag - must be a valid directory path
50 .flag(
51 Flag::new("log-dir")
52 .usage("Directory for log files")
53 .value_type(FlagType::Directory)
54 .default(FlagValue::String("/tmp".to_string())),
55 )
56 // StringArray flag - can be specified multiple times
57 .flag(
58 Flag::new("tags")
59 .short('t')
60 .usage("Tags for the server (can be specified multiple times)")
61 .value_type(FlagType::StringArray),
62 )
63 // Flag with RequiredIf constraint
64 .flag(
65 Flag::new("ssl-cert")
66 .usage("SSL certificate file")
67 .value_type(FlagType::File)
68 .constraint(FlagConstraint::RequiredIf("ssl".to_string())),
69 )
70 // Flag with RequiredIf constraint
71 .flag(
72 Flag::new("ssl-key")
73 .usage("SSL private key file")
74 .value_type(FlagType::File)
75 .constraint(FlagConstraint::RequiredIf("ssl".to_string())),
76 )
77 // Flag that triggers the RequiredIf constraints above
78 .flag(
79 Flag::new("ssl")
80 .usage("Enable SSL/TLS")
81 .value_type(FlagType::Bool),
82 )
83 // Flag with ConflictsWith constraint
84 .flag(
85 Flag::new("debug")
86 .short('d')
87 .usage("Enable debug mode")
88 .value_type(FlagType::Bool)
89 .constraint(FlagConstraint::ConflictsWith(vec!["quiet".to_string()])),
90 )
91 // Flag that conflicts with debug
92 .flag(
93 Flag::new("quiet")
94 .short('q')
95 .usage("Quiet mode (minimal output)")
96 .value_type(FlagType::Bool)
97 .constraint(FlagConstraint::ConflictsWith(vec!["debug".to_string()])),
98 )
99 // Flag with Requires constraint
100 .flag(
101 Flag::new("auth-token")
102 .usage("Authentication token")
103 .value_type(FlagType::String)
104 .constraint(FlagConstraint::Requires(vec!["auth-enabled".to_string()])),
105 )
106 // Flag required by auth-token
107 .flag(
108 Flag::new("auth-enabled")
109 .usage("Enable authentication")
110 .value_type(FlagType::Bool),
111 )
112 // Required flag
113 .flag(
114 Flag::new("name")
115 .short('n')
116 .usage("Server name")
117 .value_type(FlagType::String)
118 .required(),
119 )
120 .run(|ctx| {
121 // Display configuration
122 println!("=== Server Configuration ===\n");
123
124 println!("Server Name: {}", ctx.flag("name").unwrap());
125 println!(
126 "Environment: {}",
127 ctx.flag("environment")
128 .unwrap_or(&"development".to_string())
129 );
130 println!("Port: {}", ctx.flag("port").unwrap_or(&"8080".to_string()));
131
132 if let Some(config) = ctx.flag("config") {
133 println!("Config File: {}", config);
134 }
135
136 println!(
137 "Log Directory: {}",
138 ctx.flag("log-dir").unwrap_or(&"/tmp".to_string())
139 );
140
141 if let Some(tags) = ctx.flag("tags") {
142 println!("Tags: {}", tags);
143 }
144
145 if ctx.flag("ssl").map(|v| v == "true").unwrap_or(false) {
146 println!("\nSSL Configuration:");
147 println!(" Certificate: {}", ctx.flag("ssl-cert").unwrap());
148 println!(" Private Key: {}", ctx.flag("ssl-key").unwrap());
149 }
150
151 if ctx.flag("debug").map(|v| v == "true").unwrap_or(false) {
152 println!("\nDebug mode: ENABLED");
153 } else if ctx.flag("quiet").map(|v| v == "true").unwrap_or(false) {
154 println!("\nQuiet mode: ENABLED");
155 }
156
157 if ctx
158 .flag("auth-enabled")
159 .map(|v| v == "true")
160 .unwrap_or(false)
161 {
162 println!("\nAuthentication: ENABLED");
163 if let Some(token) = ctx.flag("auth-token") {
164 println!(" Token: {}", token);
165 }
166 }
167
168 println!("\nServer would start with the above configuration...");
169 Ok(())
170 })
171 .build();
172
173 println!("=== Advanced Flags Demo ===\n");
174 println!("This demo showcases advanced flag features in flag-rs.\n");
175 println!("Try these commands to see different features:");
176 println!();
177 println!("1. Basic usage with required flag:");
178 println!(
179 " {} --name myserver",
180 std::env::args().next().unwrap_or_default()
181 );
182 println!();
183 println!("2. Choice flag validation:");
184 println!(
185 " {} --name myserver --environment invalid (will fail)",
186 std::env::args().next().unwrap_or_default()
187 );
188 println!(
189 " {} --name myserver --environment production",
190 std::env::args().next().unwrap_or_default()
191 );
192 println!();
193 println!("3. Range validation:");
194 println!(
195 " {} --name myserver --port 80 (will fail - below 1024)",
196 std::env::args().next().unwrap_or_default()
197 );
198 println!(
199 " {} --name myserver --port 3000",
200 std::env::args().next().unwrap_or_default()
201 );
202 println!();
203 println!("4. File/Directory validation:");
204 println!(
205 " {} --name myserver --config ./Cargo.toml",
206 std::env::args().next().unwrap_or_default()
207 );
208 println!(
209 " {} --name myserver --log-dir ./src",
210 std::env::args().next().unwrap_or_default()
211 );
212 println!();
213 println!("5. Flag constraints:");
214 println!(
215 " {} --name myserver --ssl (will fail - missing cert and key)",
216 std::env::args().next().unwrap_or_default()
217 );
218 println!(
219 " {} --name myserver --ssl --ssl-cert cert.pem --ssl-key key.pem",
220 std::env::args().next().unwrap_or_default()
221 );
222 println!(
223 " {} --name myserver --debug --quiet (will fail - conflicts)",
224 std::env::args().next().unwrap_or_default()
225 );
226 println!(
227 " {} --name myserver --auth-token secret (will fail - requires --auth-enabled)",
228 std::env::args().next().unwrap_or_default()
229 );
230 println!();
231 println!("6. View help:");
232 println!(" {} --help", std::env::args().next().unwrap_or_default());
233 println!("\n---\n");
234
235 let args: Vec<String> = std::env::args().skip(1).collect();
236 if let Err(e) = app.execute(args) {
237 eprintln!("{}", e);
238 std::process::exit(1);
239 }
240}7fn main() {
8 let app = CommandBuilder::new("helpdemo")
9 .short("A demo of enhanced help formatting")
10 .long("This is a comprehensive demonstration of the enhanced help formatting system in flag-rs. \
11 It shows how help text is beautifully formatted with proper grouping, constraint \
12 information, and visual hierarchy.")
13 .group_id("utilities")
14 .example("helpdemo config --format=yaml --output=config.yaml")
15 .example("helpdemo process --workers=4 --verbose data.json")
16 .example("helpdemo serve --port=8080 --ssl --ssl-cert=cert.pem")
17 .flag(
18 Flag::new("verbose")
19 .short('v')
20 .value_type(FlagType::Bool)
21 .usage("Enable verbose output (can be repeated for more verbosity)"),
22 )
23 .flag(
24 Flag::new("config")
25 .short('c')
26 .value_type(FlagType::File)
27 .default(FlagValue::String("~/.config/app.toml".to_string()))
28 .usage("Configuration file path"),
29 )
30 .flag(
31 Flag::new("workers")
32 .short('w')
33 .value_type(FlagType::Range(1, 16))
34 .default(FlagValue::Int(4))
35 .usage("Number of worker threads"),
36 )
37 .flag(
38 Flag::new("timeout")
39 .short('t')
40 .value_type(FlagType::Int)
41 .default(FlagValue::Int(30))
42 .usage("Request timeout in seconds"),
43 )
44 .subcommand(
45 CommandBuilder::new("config")
46 .short("Manage configuration")
47 .long("The config command allows you to view, edit, and validate configuration files. \
48 It supports multiple output formats and can merge configurations from various sources.")
49 .alias("cfg")
50 .alias("conf")
51 .example("helpdemo config show")
52 .example("helpdemo config validate --strict")
53 .flag(
54 Flag::new("format")
55 .short('f')
56 .value_type(FlagType::Choice(vec![
57 "json".to_string(),
58 "yaml".to_string(),
59 "toml".to_string(),
60 "ini".to_string(),
61 ]))
62 .default(FlagValue::String("yaml".to_string()))
63 .usage("Configuration file format"),
64 )
65 .flag(
66 Flag::new("output")
67 .short('o')
68 .value_type(FlagType::String)
69 .required()
70 .usage("Output file path"),
71 )
72 .flag(
73 Flag::new("merge")
74 .value_type(FlagType::StringArray)
75 .usage("Additional config files to merge"),
76 )
77 .flag(
78 Flag::new("strict")
79 .value_type(FlagType::Bool)
80 .usage("Enable strict validation mode"),
81 )
82 .subcommand(
83 CommandBuilder::new("show")
84 .short("Display current configuration")
85 .run(|_| Ok(()))
86 .build(),
87 )
88 .subcommand(
89 CommandBuilder::new("validate")
90 .short("Validate configuration syntax")
91 .run(|_| Ok(()))
92 .build(),
93 )
94 .build(),
95 )
96 .subcommand(
97 CommandBuilder::new("process")
98 .short("Process data files")
99 .group_id("operations")
100 .flag(
101 Flag::new("input")
102 .short('i')
103 .value_type(FlagType::File)
104 .required()
105 .usage("Input data file"),
106 )
107 .flag(
108 Flag::new("output")
109 .short('o')
110 .value_type(FlagType::String)
111 .required()
112 .usage("Output file path"),
113 )
114 .flag(
115 Flag::new("format")
116 .short('f')
117 .value_type(FlagType::Choice(vec![
118 "csv".to_string(),
119 "json".to_string(),
120 "parquet".to_string(),
121 ]))
122 .usage("Output format"),
123 )
124 .flag(
125 Flag::new("compress")
126 .value_type(FlagType::Bool)
127 .usage("Compress output"),
128 )
129 .flag(
130 Flag::new("compression-level")
131 .value_type(FlagType::Range(1, 9))
132 .default(FlagValue::Int(6))
133 .constraint(FlagConstraint::RequiredIf("compress".to_string()))
134 .usage("Compression level"),
135 )
136 .run(|_| Ok(()))
137 .build(),
138 )
139 .subcommand(
140 CommandBuilder::new("serve")
141 .short("Start the server")
142 .group_id("operations")
143 .flag(
144 Flag::new("port")
145 .short('p')
146 .value_type(FlagType::Range(1, 65535))
147 .default(FlagValue::Int(8080))
148 .usage("Port to listen on"),
149 )
150 .flag(
151 Flag::new("host")
152 .short('h')
153 .value_type(FlagType::String)
154 .default(FlagValue::String("127.0.0.1".to_string()))
155 .usage("Host to bind to"),
156 )
157 .flag(
158 Flag::new("ssl")
159 .value_type(FlagType::Bool)
160 .usage("Enable SSL/TLS"),
161 )
162 .flag(
163 Flag::new("ssl-cert")
164 .value_type(FlagType::File)
165 .constraint(FlagConstraint::RequiredIf("ssl".to_string()))
166 .usage("SSL certificate file"),
167 )
168 .flag(
169 Flag::new("ssl-key")
170 .value_type(FlagType::File)
171 .constraint(FlagConstraint::RequiredIf("ssl".to_string()))
172 .usage("SSL private key file"),
173 )
174 .flag(
175 Flag::new("http2")
176 .value_type(FlagType::Bool)
177 .constraint(FlagConstraint::Requires(vec!["ssl".to_string()]))
178 .usage("Enable HTTP/2 support"),
179 )
180 .flag(
181 Flag::new("debug")
182 .short('d')
183 .value_type(FlagType::Bool)
184 .constraint(FlagConstraint::ConflictsWith(vec!["production".to_string()]))
185 .usage("Enable debug mode"),
186 )
187 .flag(
188 Flag::new("production")
189 .value_type(FlagType::Bool)
190 .usage("Enable production mode"),
191 )
192 .run(|_| Ok(()))
193 .build(),
194 )
195 .build();
196
197 println!("This example demonstrates enhanced help formatting.");
198 println!("Try these commands to see different help layouts:\n");
199 println!(" # Main help with command groups");
200 println!(" cargo run --example help_formatting_demo -- --help\n");
201
202 println!(" # Subcommand help with required flags");
203 println!(" cargo run --example help_formatting_demo -- config --help\n");
204
205 println!(" # Subcommand with flag constraints");
206 println!(" cargo run --example help_formatting_demo -- serve --help\n");
207
208 let args: Vec<String> = std::env::args().skip(1).collect();
209 if !args.is_empty() {
210 if let Err(e) = app.execute(args) {
211 eprintln!("{}", e);
212 std::process::exit(1);
213 }
214 }
215}Sourcepub fn completion<F>(self, completion: F) -> Self
pub fn completion<F>(self, completion: F) -> Self
Sets a completion function for this flag’s values
§Arguments
completion- A function that generates completions for flag values
§Examples
use flag_rs::flag::Flag;
use flag_rs::completion::CompletionResult;
let flag = Flag::file("config")
.completion(|ctx, prefix| {
// In a real application, you might list config files
let configs = vec!["default.conf", "production.conf", "test.conf"];
Ok(CompletionResult::new().extend(
configs.into_iter()
.filter(|c| c.starts_with(prefix))
.map(String::from)
))
});Examples found in repository?
4fn main() {
5 let app = CommandBuilder::new("deploy")
6 .short("Deploy application to various environments")
7 .flag(
8 Flag::choice("environment", &["dev", "staging", "prod"])
9 .short('e')
10 .usage("Target environment")
11 .required()
12 .completion(|_ctx, prefix| {
13 // Dynamic completion - could check available environments
14 let environments = vec![
15 ("dev", "Development environment"),
16 ("staging", "Staging environment (pre-production)"),
17 ("prod", "Production environment"),
18 ];
19
20 let mut result = CompletionResult::new();
21 for (env, desc) in environments {
22 if env.starts_with(prefix) {
23 result = result.add_with_description(env.to_string(), desc.to_string());
24 }
25 }
26
27 // Add active help if no prefix
28 if prefix.is_empty() {
29 result = result.add_help_text("Available environments: dev, staging, prod");
30 }
31
32 Ok(result)
33 }),
34 )
35 .flag(
36 Flag::file("config")
37 .short('c')
38 .usage("Configuration file")
39 .default_str("config.yaml")
40 .completion(|_ctx, prefix| {
41 // In a real app, you might list files in the config directory
42 let configs = vec![
43 "config.yaml",
44 "config.dev.yaml",
45 "config.staging.yaml",
46 "config.prod.yaml",
47 "secrets.yaml",
48 ];
49
50 Ok(CompletionResult::new().extend(
51 configs
52 .into_iter()
53 .filter(|c| c.starts_with(prefix))
54 .map(String::from),
55 ))
56 }),
57 )
58 .flag(
59 Flag::directory("output")
60 .short('o')
61 .usage("Output directory for deployment artifacts")
62 .completion(|_ctx, prefix| {
63 // In a real app, you might list actual directories
64 let dirs = vec!["./build", "./dist", "./output", "/tmp/deploy"];
65
66 Ok(CompletionResult::new().extend(
67 dirs.into_iter()
68 .filter(|d| d.starts_with(prefix))
69 .map(String::from),
70 ))
71 }),
72 )
73 .flag(
74 Flag::string_slice("service")
75 .short('s')
76 .usage("Services to deploy (can be specified multiple times)")
77 .completion(|ctx, prefix| {
78 // Context-aware completion based on environment
79 let env = ctx.flag_str_or("environment", "dev");
80
81 let services = match env {
82 "dev" => vec!["api", "web", "worker", "scheduler", "debug-panel"],
83 "staging" => vec!["api", "web", "worker", "scheduler"],
84 "prod" => vec!["api", "web", "worker"],
85 _ => vec!["api", "web"],
86 };
87
88 let mut result = CompletionResult::new().extend(
89 services
90 .into_iter()
91 .filter(|s| s.starts_with(prefix))
92 .map(String::from),
93 );
94
95 // Add context-aware help
96 if prefix.is_empty() {
97 result = result
98 .add_help_text(format!("Available services for {} environment", env));
99 }
100
101 Ok(result)
102 }),
103 )
104 .flag(
105 Flag::range("replicas", 1, 10)
106 .short('r')
107 .usage("Number of replicas to deploy")
108 .default_int(2)
109 .completion(|_ctx, _prefix| {
110 // Suggest common replica counts
111 Ok(CompletionResult::new()
112 .add_with_description(
113 "1".to_string(),
114 "Single instance (dev/test)".to_string(),
115 )
116 .add_with_description("2".to_string(), "Basic redundancy".to_string())
117 .add_with_description("3".to_string(), "Standard production".to_string())
118 .add_with_description("5".to_string(), "High availability".to_string()))
119 }),
120 )
121 .run(|ctx| {
122 let env = ctx.flag_str_or("environment", "dev");
123 let config = ctx.flag_str_or("config", "config.yaml");
124 let replicas = ctx.flag_int_or("replicas", 2);
125
126 println!("Deploying to {} environment", env);
127 println!("Using configuration: {}", config);
128 println!("Replica count: {}", replicas);
129
130 if let Some(output) = ctx.flag("output") {
131 println!("Output directory: {}", output);
132 }
133
134 if let Some(services) = ctx.flag("service") {
135 println!("Deploying services: {}", services);
136 } else {
137 println!("Deploying all services");
138 }
139
140 Ok(())
141 })
142 .build();
143
144 let args: Vec<String> = std::env::args().skip(1).collect();
145 if let Err(e) = app.execute(args) {
146 eprintln!("Error: {}", e);
147 std::process::exit(1);
148 }
149}More examples
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}Source§impl Flag
impl Flag
Sourcepub fn parse_value(&self, input: &str) -> Result<FlagValue>
pub fn parse_value(&self, input: &str) -> Result<FlagValue>
Parses a string value according to this flag’s type
§Arguments
input- The string value to parse
§Returns
Returns the parsed FlagValue on success
§Errors
Returns Error::FlagParsing if the input cannot be parsed as the expected type
§Examples
use flag_rs::flag::{Flag, FlagType, FlagValue};
let int_flag = Flag::new("count").value_type(FlagType::Int);
match int_flag.parse_value("42") {
Ok(FlagValue::Int(n)) => assert_eq!(n, 42),
_ => panic!("Expected Int value"),
}
let bool_flag = Flag::new("verbose").value_type(FlagType::Bool);
match bool_flag.parse_value("true") {
Ok(FlagValue::Bool(b)) => assert!(b),
_ => panic!("Expected Bool value"),
}