1use std::{env, path::PathBuf, process, str::FromStr, sync::LazyLock};
2
3use clap::Args;
4pub use toml::Value;
5use tracing::Level;
6
7use crate::{IncludePattern, LeanMode, OverflowMode, PointerWidth, RawDerefMode, SmtSolver};
8
9const FLUX_FLAG_PREFIX: &str = "-F";
10
11macro_rules! flux_arg {
12 ($name:literal) => {
13 concat!("F", $name)
14 };
15}
16
17pub const EXIT_FAILURE: i32 = 2;
19
20#[derive(Args)]
21#[command(next_help_heading = "Flux-Specific Flags (Not Yet Supported)")]
22pub struct Flags {
23 #[arg(
25 long = flux_arg!("log-dir"),
26 value_name = "PATH",
27 default_value = "./log/",
28 )]
29 pub log_dir: PathBuf,
30 #[arg(
32 long = flux_arg!("lean-dir"),
33 value_name = "PATH",
34 default_value = "./"
35 )]
36 pub lean_dir: PathBuf,
37 #[arg(
39 long = flux_arg!("lean-project"),
40 value_name = "NAME",
41 default_value = "lean_proofs"
42 )]
43 pub lean_project: String,
44 #[arg(long = flux_arg!("include"), value_name = "PATTERN", value_parser = panicking_parser)]
46 pub include: Option<IncludePattern>,
47 #[arg(long = flux_arg!("include-trusted"), value_name = "PATTERN", value_parser = panicking_parser)]
49 pub include_trusted: Option<IncludePattern>,
50 #[arg(
52 long = flux_arg!("include-trusted-impl"),
53 value_name = "PATTERN",
54 value_parser = panicking_parser
55 )]
56 pub include_trusted_impl: Option<IncludePattern>,
57 #[arg(
60 long = flux_arg!("pointer-width"),
61 value_name = "WIDTH",
62 default_value = "64",
63 value_parser = default_pointerwidth
64 )]
65 pub pointer_width: PointerWidth,
66 #[arg(long = flux_arg!("cache"), value_name = "PATH", value_parser = panicking_parser)]
68 pub cache: Option<PathBuf>,
69 #[arg(
71 long = flux_arg!("annots"),
72 num_args = 0..=1,
73 default_missing_value = "true"
74 )]
75 pub annots: bool,
76 #[arg(
79 long = flux_arg!("timings"),
80 num_args = 0..=1,
81 default_missing_value = "true"
82 )]
83 pub timings: bool,
84 #[arg(
86 long = flux_arg!("summary"),
87 num_args = 0..=1,
88 default_missing_value = "true"
89 )]
90 pub summary: bool,
91 #[arg(
93 long = flux_arg!("solver"),
94 value_name = "SOLVER",
95 default_value = "z3",
96 value_parser = default_smtsolver
97 )]
98 pub solver: SmtSolver,
99 #[arg(
101 long = flux_arg!("scrape-quals"),
102 num_args = 0..=1,
103 default_missing_value = "true"
104 )]
105 pub scrape_quals: bool,
106 #[arg(
108 long = flux_arg!("allow-uninterpreted-cast"),
109 num_args = 0..=1,
110 default_missing_value = "true"
111 )]
112 pub allow_uninterpreted_cast: bool,
113 #[arg(
116 long = flux_arg!("smt-define-fun"),
117 num_args = 0..=1,
118 default_missing_value = "true"
119 )]
120 pub smt_define_fun: bool,
121 #[arg(
125 long = flux_arg!("check-overflow"),
126 value_name = "MODE",
127 default_value = "none",
128 value_parser = default_overflowmode
129 )]
130 pub check_overflow: OverflowMode,
131 #[arg(
133 long = flux_arg!("allow-raw-deref"),
134 value_name = "MODE",
135 default_value = "default",
136 value_parser = default_rawderefmode
137 )]
138 pub allow_raw_deref: RawDerefMode,
139 #[arg(
141 long = flux_arg!("dump-constraint"),
142 num_args = 0..=1,
143 default_missing_value = "true"
144 )]
145 pub dump_constraint: bool,
146 #[arg(long = flux_arg!("dump-checker-trace"), value_name = "LEVEL", value_parser = panicking_parser)]
148 pub dump_checker_trace: Option<tracing::Level>,
149 #[arg(
151 long = flux_arg!("dump-fhir"),
152 num_args = 0..=1,
153 default_missing_value = "true"
154 )]
155 pub dump_fhir: bool,
156 #[arg(
158 long = flux_arg!("dump-rty"),
159 num_args = 0..=1,
160 default_missing_value = "true"
161 )]
162 pub dump_rty: bool,
163 #[arg(
165 long = flux_arg!("catch-bugs"),
166 num_args = 0..=1,
167 default_missing_value = "true"
168 )]
169 pub catch_bugs: bool,
170 #[arg(
174 long = flux_arg!("verify"),
175 num_args = 0..=1,
176 default_missing_value = "false"
177 )]
178 pub verify: bool,
179 #[arg(
182 long = flux_arg!("full-compilation"),
183 num_args = 0..=1,
184 default_missing_value = "true"
185 )]
186 pub full_compilation: bool,
187 #[arg(long = flux_arg!("sysroot"), value_name = "PATH", value_parser = panicking_parser)]
189 pub sysroot: Option<PathBuf>,
190 #[arg(
192 long = flux_arg!("trusted"),
193 num_args = 0..=1,
194 default_missing_value = "false"
195 )]
196 pub trusted_default: bool,
197 #[arg(
199 long = flux_arg!("ignore"),
200 num_args = 0..=1,
201 default_missing_value = "false"
202 )]
203 pub ignore_default: bool,
204 #[arg(
205 long = flux_arg!("lean"),
206 value_name = "MODE",
207 default_value = "default",
208 value_parser = default_leanmode
209 )]
210 pub lean: LeanMode,
211 #[arg(
213 long = flux_arg!("no-panic"),
214 num_args = 0..=1,
215 default_missing_value = "false"
216 )]
217 pub no_panic: bool,
218 #[arg(
221 long = flux_arg!("std-extern-specs"),
222 num_args = 0..=1,
223 default_missing_value = "false"
224 )]
225 pub std_extern_specs: bool,
226 #[arg(
228 long = flux_arg!("flux-verbose"),
229 num_args = 0..=1,
230 default_missing_value = "false"
231 )]
232 pub flux_verbose: bool,
233}
234
235impl Default for Flags {
236 fn default() -> Self {
237 Self {
238 log_dir: PathBuf::from("./log/"),
239 lean_dir: PathBuf::from("./"),
240 lean_project: "lean_proofs".to_string(),
241 dump_constraint: false,
242 dump_checker_trace: None,
243 dump_fhir: false,
244 dump_rty: false,
245 catch_bugs: false,
246 pointer_width: PointerWidth::default(),
247 include: None,
248 include_trusted: None,
249 include_trusted_impl: None,
250 cache: None,
251 check_overflow: OverflowMode::default(),
252 allow_raw_deref: RawDerefMode::default(),
253 scrape_quals: false,
254 allow_uninterpreted_cast: false,
255 solver: SmtSolver::default(),
256 smt_define_fun: false,
257 annots: false,
258 timings: false,
259 summary: true,
260 verify: false,
261 full_compilation: false,
262 sysroot: None,
263 trusted_default: false,
264 ignore_default: false,
265 lean: LeanMode::default(),
266 no_panic: false,
267 std_extern_specs: false,
268 flux_verbose: false,
269 }
270 }
271}
272
273pub(crate) static FLAGS: LazyLock<Flags> = LazyLock::new(|| {
274 let mut flags = Flags::default();
275 let mut includes: Vec<String> = Vec::new();
276 let mut trusteds: Vec<String> = Vec::new();
277 let mut trusted_impls: Vec<String> = Vec::new();
278 for arg in env::args() {
279 let Some((key, value)) = parse_flux_arg(&arg) else { continue };
280
281 let result = match key {
282 "log-dir" => parse_path_buf(&mut flags.log_dir, value),
283 "lean-dir" => parse_path_buf(&mut flags.lean_dir, value),
284 "lean-project" => parse_string(&mut flags.lean_project, value),
285 "dump-constraint" => parse_bool(&mut flags.dump_constraint, value),
286 "dump-checker-trace" => parse_opt_level(&mut flags.dump_checker_trace, value),
287 "dump-fhir" => parse_bool(&mut flags.dump_fhir, value),
288 "dump-rty" => parse_bool(&mut flags.dump_rty, value),
289 "catch-bugs" => parse_bool(&mut flags.catch_bugs, value),
290 "pointer-width" => parse_pointer_width(&mut flags.pointer_width, value),
291 "check-overflow" => parse_overflow(&mut flags.check_overflow, value),
292 "allow-raw-deref" => parse_raw_deref(&mut flags.allow_raw_deref, value),
293 "scrape-quals" => parse_bool(&mut flags.scrape_quals, value),
294 "allow-uninterpreted-cast" => parse_bool(&mut flags.allow_uninterpreted_cast, value),
295 "solver" => parse_solver(&mut flags.solver, value),
296 "smt-define-fun" => parse_bool(&mut flags.smt_define_fun, value),
297 "annots" => parse_bool(&mut flags.annots, value),
298 "timings" => parse_bool(&mut flags.timings, value),
299 "summary" => parse_bool(&mut flags.summary, value),
300 "cache" => parse_opt_path_buf(&mut flags.cache, value),
301 "include" => parse_opt_include(&mut includes, value),
302 "include-trusted" => parse_opt_include(&mut trusteds, value),
303 "include-trusted-impl" => parse_opt_include(&mut trusted_impls, value),
304 "verify" => parse_bool(&mut flags.verify, value),
305 "full-compilation" => parse_bool(&mut flags.full_compilation, value),
306 "sysroot" => parse_opt_path_buf(&mut flags.sysroot, value),
307 "trusted" => parse_bool(&mut flags.trusted_default, value),
308 "ignore" => parse_bool(&mut flags.ignore_default, value),
309 "lean" => parse_lean_mode(&mut flags.lean, value),
310 "no-panic" => parse_bool(&mut flags.no_panic, value),
311 "std-extern-specs" => parse_bool(&mut flags.std_extern_specs, value),
312 "flux-verbose" => parse_bool(&mut flags.flux_verbose, value),
313 _ => {
314 eprintln!("error: unknown flux option: `{key}`");
315 process::exit(EXIT_FAILURE);
316 }
317 };
318 if let Err(reason) = result {
319 eprintln!("error: incorrect value for flux option `{key}` - `{reason}`");
320 process::exit(1);
321 }
322 }
323 if !includes.is_empty() {
324 let include = IncludePattern::new(includes).unwrap_or_else(|err| {
325 eprintln!("error: invalid include pattern: {err}");
326 process::exit(1);
327 });
328 flags.include = Some(include);
329 }
330 if !trusteds.is_empty() {
331 let trusted = IncludePattern::new(trusteds).unwrap_or_else(|err| {
332 eprintln!("error: invalid trusted pattern: {err}");
333 process::exit(1);
334 });
335 flags.include_trusted = Some(trusted);
336 }
337 if !trusted_impls.is_empty() {
338 let trusted_impl = IncludePattern::new(trusted_impls).unwrap_or_else(|err| {
339 eprintln!("error: invalid trusted-impl pattern: {err}");
340 process::exit(1);
341 });
342 flags.include_trusted_impl = Some(trusted_impl);
343 }
344 flags
345});
346
347pub fn is_flux_arg(arg: &str) -> bool {
348 parse_flux_arg(arg).is_some()
349}
350
351fn parse_flux_arg(arg: &str) -> Option<(&str, Option<&str>)> {
352 let arg = arg.strip_prefix(FLUX_FLAG_PREFIX)?;
353 if arg.is_empty() {
354 return None;
355 }
356 if let Some((k, v)) = arg.split_once('=') { Some((k, Some(v))) } else { Some((arg, None)) }
357}
358
359fn parse_bool(slot: &mut bool, v: Option<&str>) -> Result<(), &'static str> {
360 match v {
361 Some("y") | Some("yes") | Some("on") | Some("true") | None => {
362 *slot = true;
363 Ok(())
364 }
365 Some("n") | Some("no") | Some("off") | Some("false") => {
366 *slot = false;
367 Ok(())
368 }
369 _ => {
370 Err(
371 "expected no value or one of `y`, `yes`, `on`, `true`, `n`, `no`, `off`, or `false`",
372 )
373 }
374 }
375}
376
377fn parse_string(slot: &mut String, v: Option<&str>) -> Result<(), &'static str> {
378 match v {
379 Some(s) => {
380 *slot = s.to_string();
381 Ok(())
382 }
383 None => Err("expected a string"),
384 }
385}
386
387fn parse_path_buf(slot: &mut PathBuf, v: Option<&str>) -> Result<(), &'static str> {
388 match v {
389 Some(s) => {
390 *slot = PathBuf::from(s);
391 Ok(())
392 }
393 None => Err("expected a path"),
394 }
395}
396
397fn parse_pointer_width(slot: &mut PointerWidth, v: Option<&str>) -> Result<(), &'static str> {
398 match v {
399 Some(s) => {
400 *slot = s.parse()?;
401 Ok(())
402 }
403 _ => Err(PointerWidth::ERROR),
404 }
405}
406
407fn parse_lean_mode(slot: &mut LeanMode, v: Option<&str>) -> Result<(), &'static str> {
408 match v {
409 Some(s) => {
410 *slot = s.parse()?;
411 Ok(())
412 }
413 _ => Err(LeanMode::ERROR),
414 }
415}
416
417fn parse_overflow(slot: &mut OverflowMode, v: Option<&str>) -> Result<(), &'static str> {
418 match v {
419 Some(s) => {
420 *slot = s.parse()?;
421 Ok(())
422 }
423 _ => Err(OverflowMode::ERROR),
424 }
425}
426
427fn parse_raw_deref(slot: &mut RawDerefMode, v: Option<&str>) -> Result<(), &'static str> {
428 match v {
429 Some(s) => {
430 *slot = s.parse()?;
431 Ok(())
432 }
433 _ => Err(RawDerefMode::ERROR),
434 }
435}
436
437fn parse_solver(slot: &mut SmtSolver, v: Option<&str>) -> Result<(), &'static str> {
438 match v {
439 Some(s) => {
440 *slot = s.parse()?;
441 Ok(())
442 }
443 _ => Err(SmtSolver::ERROR),
444 }
445}
446
447fn parse_opt_path_buf(slot: &mut Option<PathBuf>, v: Option<&str>) -> Result<(), &'static str> {
448 match v {
449 Some(s) => {
450 *slot = Some(PathBuf::from(s));
451 Ok(())
452 }
453 None => Err("expected a path"),
454 }
455}
456
457fn parse_opt_level(slot: &mut Option<Level>, v: Option<&str>) -> Result<(), &'static str> {
458 match v {
459 Some(s) => {
460 *slot = Some(Level::from_str(s).map_err(|_| "invalid level")?);
461 Ok(())
462 }
463 None => Err("a level"),
464 }
465}
466
467fn parse_opt_include(slot: &mut Vec<String>, v: Option<&str>) -> Result<(), &'static str> {
468 if let Some(include) = v {
469 slot.push(include.to_string());
470 }
471 Ok(())
472}
473
474fn panicking_parser(_s: &str) -> Result<(), String> {
475 panic!("Parsing flux args from cli is not yet supported.");
476}
477
478fn default_pointerwidth(_s: &str) -> Result<PointerWidth, String> {
479 Ok(PointerWidth::default())
480}
481
482fn default_overflowmode(_s: &str) -> Result<OverflowMode, String> {
483 Ok(OverflowMode::default())
484}
485
486fn default_rawderefmode(_s: &str) -> Result<RawDerefMode, String> {
487 Ok(RawDerefMode::default())
488}
489
490fn default_smtsolver(_s: &str) -> Result<SmtSolver, String> {
491 Ok(SmtSolver::default())
492}
493
494fn default_leanmode(_s: &str) -> Result<LeanMode, String> {
495 Ok(LeanMode::default())
496}