1use std::{env, path::PathBuf, process, str::FromStr, sync::LazyLock};
2
3pub use toml::Value;
4use tracing::Level;
5
6use crate::{IncludePattern, OverflowMode, PointerWidth, SmtSolver};
7
8const FLUX_FLAG_PREFIX: &str = "-F";
9
10pub const EXIT_FAILURE: i32 = 2;
12
13pub struct Flags {
14 pub log_dir: PathBuf,
16 pub include: Option<IncludePattern>,
18 pub pointer_width: PointerWidth,
21 pub cache: Option<PathBuf>,
23 pub annots: bool,
25 pub timings: bool,
28 pub summary: bool,
30 pub solver: SmtSolver,
32 pub scrape_quals: bool,
34 pub allow_uninterpreted_cast: bool,
36 pub smt_define_fun: bool,
39 pub check_overflow: OverflowMode,
43 pub dump_constraint: bool,
45 pub dump_checker_trace: Option<tracing::Level>,
47 pub dump_fhir: bool,
49 pub dump_rty: bool,
51 pub catch_bugs: bool,
53 pub verify: bool,
57 pub full_compilation: bool,
60 pub trusted_default: bool,
62 pub ignore_default: bool,
64 pub emit_lean_defs: bool,
65}
66
67impl Default for Flags {
68 fn default() -> Self {
69 Self {
70 log_dir: PathBuf::from("./log/"),
71 dump_constraint: false,
72 dump_checker_trace: None,
73 dump_fhir: false,
74 dump_rty: false,
75 catch_bugs: false,
76 pointer_width: PointerWidth::default(),
77 include: None,
78 cache: None,
79 check_overflow: OverflowMode::default(),
80 scrape_quals: false,
81 allow_uninterpreted_cast: false,
82 solver: SmtSolver::default(),
83 smt_define_fun: false,
84 annots: false,
85 timings: false,
86 summary: true,
87 verify: false,
88 full_compilation: false,
89 trusted_default: false,
90 ignore_default: false,
91 emit_lean_defs: false,
92 }
93 }
94}
95
96pub(crate) static FLAGS: LazyLock<Flags> = LazyLock::new(|| {
97 let mut flags = Flags::default();
98 let mut includes: Vec<String> = Vec::new();
99 for arg in env::args() {
100 let Some((key, value)) = parse_flux_arg(&arg) else { continue };
101
102 let result = match key {
103 "log-dir" => parse_path_buf(&mut flags.log_dir, value),
104 "dump-constraint" => parse_bool(&mut flags.dump_constraint, value),
105 "dump-checker-trace" => parse_opt_level(&mut flags.dump_checker_trace, value),
106 "dump-fhir" => parse_bool(&mut flags.dump_fhir, value),
107 "dump-rty" => parse_bool(&mut flags.dump_rty, value),
108 "catch-bugs" => parse_bool(&mut flags.catch_bugs, value),
109 "pointer-width" => parse_pointer_width(&mut flags.pointer_width, value),
110 "check-overflow" => parse_overflow(&mut flags.check_overflow, value),
111 "scrape-quals" => parse_bool(&mut flags.scrape_quals, value),
112 "allow-uninterpreted-cast" => parse_bool(&mut flags.allow_uninterpreted_cast, value),
113 "solver" => parse_solver(&mut flags.solver, value),
114 "smt-define-fun" => parse_bool(&mut flags.smt_define_fun, value),
115 "annots" => parse_bool(&mut flags.annots, value),
116 "timings" => parse_bool(&mut flags.timings, value),
117 "summary" => parse_bool(&mut flags.summary, value),
118 "cache" => parse_opt_path_buf(&mut flags.cache, value),
119 "include" => parse_opt_include(&mut includes, value),
120 "verify" => parse_bool(&mut flags.verify, value),
121 "full-compilation" => parse_bool(&mut flags.full_compilation, value),
122 "trusted" => parse_bool(&mut flags.trusted_default, value),
123 "ignore" => parse_bool(&mut flags.ignore_default, value),
124 "emit_lean_defs" => parse_bool(&mut flags.emit_lean_defs, value),
125 _ => {
126 eprintln!("error: unknown flux option: `{key}`");
127 process::exit(EXIT_FAILURE);
128 }
129 };
130 if let Err(reason) = result {
131 eprintln!("error: incorrect value for flux option `{key}` - `{reason}`");
132 process::exit(1);
133 }
134 }
135 if !includes.is_empty() {
136 let include = IncludePattern::new(includes).unwrap_or_else(|err| {
137 eprintln!("error: invalid include pattern: {err}");
138 process::exit(1);
139 });
140 flags.include = Some(include);
141 }
142 flags
143});
144
145pub fn is_flux_arg(arg: &str) -> bool {
146 parse_flux_arg(arg).is_some()
147}
148
149fn parse_flux_arg(arg: &str) -> Option<(&str, Option<&str>)> {
150 let arg = arg.strip_prefix(FLUX_FLAG_PREFIX)?;
151 if arg.is_empty() {
152 return None;
153 }
154 if let Some((k, v)) = arg.split_once('=') { Some((k, Some(v))) } else { Some((arg, None)) }
155}
156
157fn parse_bool(slot: &mut bool, v: Option<&str>) -> Result<(), &'static str> {
158 match v {
159 Some("y") | Some("yes") | Some("on") | Some("true") | None => {
160 *slot = true;
161 Ok(())
162 }
163 Some("n") | Some("no") | Some("off") | Some("false") => {
164 *slot = false;
165 Ok(())
166 }
167 _ => {
168 Err(
169 "expected no value or one of `y`, `yes`, `on`, `true`, `n`, `no`, `off`, or `false`",
170 )
171 }
172 }
173}
174
175fn parse_path_buf(slot: &mut PathBuf, v: Option<&str>) -> Result<(), &'static str> {
176 match v {
177 Some(s) => {
178 *slot = PathBuf::from(s);
179 Ok(())
180 }
181 None => Err("a path"),
182 }
183}
184
185fn parse_pointer_width(slot: &mut PointerWidth, v: Option<&str>) -> Result<(), &'static str> {
186 match v {
187 Some(s) => {
188 *slot = s.parse()?;
189 Ok(())
190 }
191 _ => Err(PointerWidth::ERROR),
192 }
193}
194
195fn parse_overflow(slot: &mut OverflowMode, v: Option<&str>) -> Result<(), &'static str> {
196 match v {
197 Some(s) => {
198 *slot = s.parse()?;
199 Ok(())
200 }
201 _ => Err(OverflowMode::ERROR),
202 }
203}
204
205fn parse_solver(slot: &mut SmtSolver, v: Option<&str>) -> Result<(), &'static str> {
206 match v {
207 Some(s) => {
208 *slot = s.parse()?;
209 Ok(())
210 }
211 _ => Err(SmtSolver::ERROR),
212 }
213}
214
215fn parse_opt_path_buf(slot: &mut Option<PathBuf>, v: Option<&str>) -> Result<(), &'static str> {
216 match v {
217 Some(s) => {
218 *slot = Some(PathBuf::from(s));
219 Ok(())
220 }
221 None => Err("a path"),
222 }
223}
224
225fn parse_opt_level(slot: &mut Option<Level>, v: Option<&str>) -> Result<(), &'static str> {
226 match v {
227 Some(s) => {
228 *slot = Some(Level::from_str(s).map_err(|_| "invalid level")?);
229 Ok(())
230 }
231 None => Err("a level"),
232 }
233}
234
235fn parse_opt_include(slot: &mut Vec<String>, v: Option<&str>) -> Result<(), &'static str> {
236 if let Some(include) = v {
237 slot.push(include.to_string());
238 }
239 Ok(())
240}