1use std::{env, path::PathBuf, process, str::FromStr, sync::LazyLock};
2
3pub use toml::Value;
4use tracing::Level;
5
6use crate::{IncludePattern, LeanMode, OverflowMode, PointerWidth, RawDerefMode, 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 lean_dir: PathBuf,
18 pub lean_project: String,
20 pub include: Option<IncludePattern>,
22 pub include_trusted: Option<IncludePattern>,
24 pub include_trusted_impl: Option<IncludePattern>,
26 pub pointer_width: PointerWidth,
29 pub cache: Option<PathBuf>,
31 pub annots: bool,
33 pub timings: bool,
36 pub summary: bool,
38 pub solver: SmtSolver,
40 pub scrape_quals: bool,
42 pub allow_uninterpreted_cast: bool,
44 pub smt_define_fun: bool,
47 pub check_overflow: OverflowMode,
51 pub allow_raw_deref: RawDerefMode,
53 pub dump_constraint: bool,
55 pub dump_checker_trace: Option<tracing::Level>,
57 pub dump_fhir: bool,
59 pub dump_rty: bool,
61 pub catch_bugs: bool,
63 pub verify: bool,
67 pub full_compilation: bool,
70 pub sysroot: Option<PathBuf>,
72 pub trusted_default: bool,
74 pub ignore_default: bool,
76 pub lean: LeanMode,
77 pub no_panic: bool,
79 pub std_extern_specs: bool,
82 pub verbose: bool,
84}
85
86impl Default for Flags {
87 fn default() -> Self {
88 Self {
89 log_dir: PathBuf::from("./log/"),
90 lean_dir: PathBuf::from("./"),
91 lean_project: "lean_proofs".to_string(),
92 dump_constraint: false,
93 dump_checker_trace: None,
94 dump_fhir: false,
95 dump_rty: false,
96 catch_bugs: false,
97 pointer_width: PointerWidth::default(),
98 include: None,
99 include_trusted: None,
100 include_trusted_impl: None,
101 cache: None,
102 check_overflow: OverflowMode::default(),
103 allow_raw_deref: RawDerefMode::default(),
104 scrape_quals: false,
105 allow_uninterpreted_cast: false,
106 solver: SmtSolver::default(),
107 smt_define_fun: false,
108 annots: false,
109 timings: false,
110 summary: true,
111 verify: false,
112 full_compilation: false,
113 sysroot: None,
114 trusted_default: false,
115 ignore_default: false,
116 lean: LeanMode::default(),
117 no_panic: false,
118 std_extern_specs: false,
119 verbose: false,
120 }
121 }
122}
123
124pub(crate) static FLAGS: LazyLock<Flags> = LazyLock::new(|| {
125 let mut flags = Flags::default();
126 let mut includes: Vec<String> = Vec::new();
127 let mut trusteds: Vec<String> = Vec::new();
128 let mut trusted_impls: Vec<String> = Vec::new();
129 for arg in env::args() {
130 let Some((key, value)) = parse_flux_arg(&arg) else { continue };
131
132 let result = match key {
133 "log-dir" => parse_path_buf(&mut flags.log_dir, value),
134 "lean-dir" => parse_path_buf(&mut flags.lean_dir, value),
135 "lean-project" => parse_string(&mut flags.lean_project, value),
136 "dump-constraint" => parse_bool(&mut flags.dump_constraint, value),
137 "dump-checker-trace" => parse_opt_level(&mut flags.dump_checker_trace, value),
138 "dump-fhir" => parse_bool(&mut flags.dump_fhir, value),
139 "dump-rty" => parse_bool(&mut flags.dump_rty, value),
140 "catch-bugs" => parse_bool(&mut flags.catch_bugs, value),
141 "pointer-width" => parse_pointer_width(&mut flags.pointer_width, value),
142 "check-overflow" => parse_overflow(&mut flags.check_overflow, value),
143 "allow-raw-deref" => parse_raw_deref(&mut flags.allow_raw_deref, value),
144 "scrape-quals" => parse_bool(&mut flags.scrape_quals, value),
145 "allow-uninterpreted-cast" => parse_bool(&mut flags.allow_uninterpreted_cast, value),
146 "solver" => parse_solver(&mut flags.solver, value),
147 "smt-define-fun" => parse_bool(&mut flags.smt_define_fun, value),
148 "annots" => parse_bool(&mut flags.annots, value),
149 "timings" => parse_bool(&mut flags.timings, value),
150 "summary" => parse_bool(&mut flags.summary, value),
151 "cache" => parse_opt_path_buf(&mut flags.cache, value),
152 "include" => parse_opt_include(&mut includes, value),
153 "include-trusted" => parse_opt_include(&mut trusteds, value),
154 "include-trusted-impl" => parse_opt_include(&mut trusted_impls, value),
155 "verify" => parse_bool(&mut flags.verify, value),
156 "full-compilation" => parse_bool(&mut flags.full_compilation, value),
157 "sysroot" => parse_opt_path_buf(&mut flags.sysroot, value),
158 "trusted" => parse_bool(&mut flags.trusted_default, value),
159 "ignore" => parse_bool(&mut flags.ignore_default, value),
160 "lean" => parse_lean_mode(&mut flags.lean, value),
161 "no-panic" => parse_bool(&mut flags.no_panic, value),
162 "std-extern-specs" => parse_bool(&mut flags.std_extern_specs, value),
163 "verbose" => parse_bool(&mut flags.verbose, value),
164 _ => {
165 eprintln!("error: unknown flux option: `{key}`");
166 process::exit(EXIT_FAILURE);
167 }
168 };
169 if let Err(reason) = result {
170 eprintln!("error: incorrect value for flux option `{key}` - `{reason}`");
171 process::exit(1);
172 }
173 }
174 if !includes.is_empty() {
175 let include = IncludePattern::new(includes).unwrap_or_else(|err| {
176 eprintln!("error: invalid include pattern: {err}");
177 process::exit(1);
178 });
179 flags.include = Some(include);
180 }
181 if !trusteds.is_empty() {
182 let trusted = IncludePattern::new(trusteds).unwrap_or_else(|err| {
183 eprintln!("error: invalid trusted pattern: {err}");
184 process::exit(1);
185 });
186 flags.include_trusted = Some(trusted);
187 }
188 if !trusted_impls.is_empty() {
189 let trusted_impl = IncludePattern::new(trusted_impls).unwrap_or_else(|err| {
190 eprintln!("error: invalid trusted-impl pattern: {err}");
191 process::exit(1);
192 });
193 flags.include_trusted_impl = Some(trusted_impl);
194 }
195 flags
196});
197
198pub fn is_flux_arg(arg: &str) -> bool {
199 parse_flux_arg(arg).is_some()
200}
201
202fn parse_flux_arg(arg: &str) -> Option<(&str, Option<&str>)> {
203 let arg = arg.strip_prefix(FLUX_FLAG_PREFIX)?;
204 if arg.is_empty() {
205 return None;
206 }
207 if let Some((k, v)) = arg.split_once('=') { Some((k, Some(v))) } else { Some((arg, None)) }
208}
209
210fn parse_bool(slot: &mut bool, v: Option<&str>) -> Result<(), &'static str> {
211 match v {
212 Some("y") | Some("yes") | Some("on") | Some("true") | None => {
213 *slot = true;
214 Ok(())
215 }
216 Some("n") | Some("no") | Some("off") | Some("false") => {
217 *slot = false;
218 Ok(())
219 }
220 _ => {
221 Err(
222 "expected no value or one of `y`, `yes`, `on`, `true`, `n`, `no`, `off`, or `false`",
223 )
224 }
225 }
226}
227
228fn parse_string(slot: &mut String, v: Option<&str>) -> Result<(), &'static str> {
229 match v {
230 Some(s) => {
231 *slot = s.to_string();
232 Ok(())
233 }
234 None => Err("expected a string"),
235 }
236}
237
238fn parse_path_buf(slot: &mut PathBuf, v: Option<&str>) -> Result<(), &'static str> {
239 match v {
240 Some(s) => {
241 *slot = PathBuf::from(s);
242 Ok(())
243 }
244 None => Err("expected a path"),
245 }
246}
247
248fn parse_pointer_width(slot: &mut PointerWidth, v: Option<&str>) -> Result<(), &'static str> {
249 match v {
250 Some(s) => {
251 *slot = s.parse()?;
252 Ok(())
253 }
254 _ => Err(PointerWidth::ERROR),
255 }
256}
257
258fn parse_lean_mode(slot: &mut LeanMode, v: Option<&str>) -> Result<(), &'static str> {
259 match v {
260 Some(s) => {
261 *slot = s.parse()?;
262 Ok(())
263 }
264 _ => Err(LeanMode::ERROR),
265 }
266}
267
268fn parse_overflow(slot: &mut OverflowMode, v: Option<&str>) -> Result<(), &'static str> {
269 match v {
270 Some(s) => {
271 *slot = s.parse()?;
272 Ok(())
273 }
274 _ => Err(OverflowMode::ERROR),
275 }
276}
277
278fn parse_raw_deref(slot: &mut RawDerefMode, v: Option<&str>) -> Result<(), &'static str> {
279 match v {
280 Some(s) => {
281 *slot = s.parse()?;
282 Ok(())
283 }
284 _ => Err(RawDerefMode::ERROR),
285 }
286}
287
288fn parse_solver(slot: &mut SmtSolver, v: Option<&str>) -> Result<(), &'static str> {
289 match v {
290 Some(s) => {
291 *slot = s.parse()?;
292 Ok(())
293 }
294 _ => Err(SmtSolver::ERROR),
295 }
296}
297
298fn parse_opt_path_buf(slot: &mut Option<PathBuf>, v: Option<&str>) -> Result<(), &'static str> {
299 match v {
300 Some(s) => {
301 *slot = Some(PathBuf::from(s));
302 Ok(())
303 }
304 None => Err("expected a path"),
305 }
306}
307
308fn parse_opt_level(slot: &mut Option<Level>, v: Option<&str>) -> Result<(), &'static str> {
309 match v {
310 Some(s) => {
311 *slot = Some(Level::from_str(s).map_err(|_| "invalid level")?);
312 Ok(())
313 }
314 None => Err("a level"),
315 }
316}
317
318fn parse_opt_include(slot: &mut Vec<String>, v: Option<&str>) -> Result<(), &'static str> {
319 if let Some(include) = v {
320 slot.push(include.to_string());
321 }
322 Ok(())
323}