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