advanced_flags_demo/
advanced_flags_demo.rs

1//! Demonstrates advanced flag features
2//!
3//! This example shows how to use:
4//! - Choice flags with predefined values
5//! - Range flags for numeric validation
6//! - File and Directory flags with path validation
7//! - Flag constraints (`RequiredIf`, `ConflictsWith`, `Requires`)
8//! - `StringArray` flags for multiple values
9
10#![allow(clippy::or_fun_call)]
11
12use flag_rs::{CommandBuilder, Flag, FlagConstraint, FlagType, FlagValue};
13
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}