Skip to content

Commit 7b95673

Browse files
authored
Let clap handle all CLI parsing errors (#2656)
This PR removes some ad-hoc error propagation that the CLI parser had for more sophisticated arguments and just uses `clap::Error` for everything.
1 parent 795d900 commit 7b95673

File tree

1 file changed

+92
-76
lines changed

1 file changed

+92
-76
lines changed

bindgen-cli/options.rs

+92-76
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
use bindgen::callbacks::TypeKind;
22
use bindgen::{
3-
builder, AliasVariation, Builder, CodegenConfig, EnumVariation,
3+
builder, Abi, AliasVariation, Builder, CodegenConfig, EnumVariation,
44
FieldVisibilityKind, Formatter, MacroTypeVariation, NonCopyUnionStyle,
55
RegexSet, RustTarget, DEFAULT_ANON_FIELDS_PREFIX, RUST_TARGET_STRINGS,
66
};
7+
use clap::error::{Error, ErrorKind};
78
use clap::{CommandFactory, Parser};
89
use std::fs::File;
9-
use std::io::{self, Error, ErrorKind};
10-
use std::path::PathBuf;
10+
use std::io;
11+
use std::path::{Path, PathBuf};
1112
use std::process::exit;
1213

1314
fn rust_target_help() -> String {
@@ -18,7 +19,9 @@ fn rust_target_help() -> String {
1819
)
1920
}
2021

21-
fn parse_codegen_config(what_to_generate: &str) -> io::Result<CodegenConfig> {
22+
fn parse_codegen_config(
23+
what_to_generate: &str,
24+
) -> Result<CodegenConfig, Error> {
2225
let mut config = CodegenConfig::empty();
2326
for what in what_to_generate.split(',') {
2427
match what {
@@ -29,9 +32,9 @@ fn parse_codegen_config(what_to_generate: &str) -> io::Result<CodegenConfig> {
2932
"constructors" => config.insert(CodegenConfig::CONSTRUCTORS),
3033
"destructors" => config.insert(CodegenConfig::DESTRUCTORS),
3134
otherwise => {
32-
return Err(Error::new(
33-
ErrorKind::Other,
34-
format!("Unknown generate item: {}", otherwise),
35+
return Err(Error::raw(
36+
ErrorKind::InvalidValue,
37+
format!("Unknown codegen item kind: {}", otherwise),
3538
));
3639
}
3740
}
@@ -40,20 +43,64 @@ fn parse_codegen_config(what_to_generate: &str) -> io::Result<CodegenConfig> {
4043
Ok(config)
4144
}
4245

46+
fn parse_rustfmt_config_path(path_str: &str) -> Result<PathBuf, Error> {
47+
let path = Path::new(path_str);
48+
49+
if !path.is_absolute() {
50+
return Err(Error::raw(
51+
ErrorKind::InvalidValue,
52+
"--rustfmt-configuration-file needs to be an absolute path!",
53+
));
54+
}
55+
56+
if path.to_str().is_none() {
57+
return Err(Error::raw(
58+
ErrorKind::InvalidUtf8,
59+
"--rustfmt-configuration-file contains non-valid UTF8 characters.",
60+
));
61+
}
62+
63+
Ok(path.to_path_buf())
64+
}
65+
66+
fn parse_abi_override(abi_override: &str) -> Result<(Abi, String), Error> {
67+
let (regex, abi_str) = abi_override
68+
.rsplit_once('=')
69+
.ok_or_else(|| Error::raw(ErrorKind::InvalidValue, "Missing `=`"))?;
70+
71+
let abi = abi_str
72+
.parse()
73+
.map_err(|err| Error::raw(ErrorKind::InvalidValue, err))?;
74+
75+
Ok((abi, regex.to_owned()))
76+
}
77+
78+
fn parse_custom_derive(
79+
custom_derive: &str,
80+
) -> Result<(Vec<String>, String), Error> {
81+
let (regex, derives) = custom_derive
82+
.rsplit_once('=')
83+
.ok_or_else(|| Error::raw(ErrorKind::InvalidValue, "Missing `=`"))?;
84+
85+
let derives = derives.split(',').map(|s| s.to_owned()).collect();
86+
87+
Ok((derives, regex.to_owned()))
88+
}
89+
4390
#[derive(Parser, Debug)]
4491
#[clap(
4592
about = "Generates Rust bindings from C/C++ headers.",
46-
override_usage = "bindgen [FLAGS] [OPTIONS] [HEADER] -- [CLANG_ARGS]...",
93+
override_usage = "bindgen <FLAGS> <OPTIONS> <HEADER> -- <CLANG_ARGS>...",
4794
trailing_var_arg = true
4895
)]
4996
struct BindgenCommand {
5097
/// C or C++ header file.
51-
header: Option<String>,
98+
header: String,
5299
/// Path to write depfile to.
53100
#[arg(long)]
54101
depfile: Option<String>,
55-
/// The default style of code used to generate enums.
56-
#[arg(long, value_name = "VARIANT")]
102+
/// The default STYLE of code used to generate enums.
103+
#[arg(long, value_name = "STYLE")]
57104
default_enum_style: Option<EnumVariation>,
58105
/// Mark any enum whose name matches REGEX as a set of bitfield flags.
59106
#[arg(long, value_name = "REGEX")]
@@ -73,11 +120,11 @@ struct BindgenCommand {
73120
/// Mark any enum whose name matches REGEX as a module of constants.
74121
#[arg(long, value_name = "REGEX")]
75122
constified_enum_module: Vec<String>,
76-
/// The default signed/unsigned type for C macro constants.
77-
#[arg(long, value_name = "VARIANT")]
123+
/// The default signed/unsigned TYPE for C macro constants.
124+
#[arg(long, value_name = "TYPE")]
78125
default_macro_constant_type: Option<MacroTypeVariation>,
79-
/// The default style of code used to generate typedefs.
80-
#[arg(long, value_name = "VARIANT")]
126+
/// The default STYLE of code used to generate typedefs.
127+
#[arg(long, value_name = "STYLE")]
81128
default_alias_style: Option<AliasVariation>,
82129
/// Mark any typedef alias whose name matches REGEX to use normal type aliasing.
83130
#[arg(long, value_name = "REGEX")]
@@ -88,7 +135,7 @@ struct BindgenCommand {
88135
/// Mark any typedef alias whose name matches REGEX to have a new type with Deref and DerefMut to the inner type.
89136
#[arg(long, value_name = "REGEX")]
90137
new_type_alias_deref: Vec<String>,
91-
/// The default style of code used to generate unions with non-Copy members. Note that ManuallyDrop was first stabilized in Rust 1.20.0.
138+
/// The default STYLE of code used to generate unions with non-Copy members. Note that ManuallyDrop was first stabilized in Rust 1.20.0.
92139
#[arg(long, value_name = "STYLE")]
93140
default_non_copy_union_style: Option<NonCopyUnionStyle>,
94141
/// Mark any union whose name matches REGEX and who has a non-Copy member to use a bindgen-generated wrapper for fields.
@@ -169,10 +216,10 @@ struct BindgenCommand {
169216
/// Output bindings for builtin definitions, e.g. __builtin_va_list.
170217
#[arg(long)]
171218
builtins: bool,
172-
/// Use the given prefix before raw types instead of ::std::os::raw.
219+
/// Use the given PREFIX before raw types instead of ::std::os::raw.
173220
#[arg(long, value_name = "PREFIX")]
174221
ctypes_prefix: Option<String>,
175-
/// Use the given prefix for anonymous fields.
222+
/// Use the given PREFIX for anonymous fields.
176223
#[arg(long, default_value = DEFAULT_ANON_FIELDS_PREFIX, value_name = "PREFIX")]
177224
anon_fields_prefix: String,
178225
/// Time the different bindgen phases and print to stderr
@@ -184,7 +231,7 @@ struct BindgenCommand {
184231
/// Output our internal IR for debugging purposes.
185232
#[arg(long)]
186233
emit_ir: bool,
187-
/// Dump graphviz dot file.
234+
/// Dump a graphviz dot file to PATH.
188235
#[arg(long, value_name = "PATH")]
189236
emit_ir_graphviz: Option<String>,
190237
/// Enable support for C++ namespaces.
@@ -232,8 +279,8 @@ struct BindgenCommand {
232279
/// Add a raw line of Rust code at the beginning of output.
233280
#[arg(long)]
234281
raw_line: Vec<String>,
235-
/// Add a raw line of Rust code to a given module.
236-
#[arg(long, number_of_values = 2, value_names = ["MODULE-NAME", "RAW-LINE"])]
282+
/// Add a RAW_LINE of Rust code to a given module with name MODULE_NAME.
283+
#[arg(long, number_of_values = 2, value_names = ["MODULE_NAME", "RAW_LINE"])]
237284
module_raw_line: Vec<String>,
238285
#[arg(long, help = rust_target_help())]
239286
rust_target: Option<RustTarget>,
@@ -277,16 +324,16 @@ struct BindgenCommand {
277324
/// `--formatter=none` instead.
278325
#[arg(long)]
279326
no_rustfmt_bindings: bool,
280-
/// Which tool should be used to format the bindings
327+
/// Which FORMATTER should be used for the bindings
281328
#[arg(
282329
long,
283330
value_name = "FORMATTER",
284331
conflicts_with = "no_rustfmt_bindings"
285332
)]
286333
formatter: Option<Formatter>,
287-
/// The absolute path to the rustfmt configuration file. The configuration file will be used for formatting the bindings. This parameter sets `formatter` to `rustfmt`.
288-
#[arg(long, value_name = "PATH", conflicts_with = "no_rustfmt_bindings")]
289-
rustfmt_configuration_file: Option<String>,
334+
/// The absolute PATH to the rustfmt configuration file. The configuration file will be used for formatting the bindings. This parameter sets `formatter` to `rustfmt`.
335+
#[arg(long, value_name = "PATH", conflicts_with = "no_rustfmt_bindings", value_parser=parse_rustfmt_config_path)]
336+
rustfmt_configuration_file: Option<PathBuf>,
290337
/// Avoid deriving PartialEq for types matching REGEX.
291338
#[arg(long, value_name = "REGEX")]
292339
no_partialeq: Vec<String>,
@@ -311,10 +358,10 @@ struct BindgenCommand {
311358
/// Use `*const [T; size]` instead of `*const T` for C arrays
312359
#[arg(long)]
313360
use_array_pointers_in_arguments: bool,
314-
/// The name to be used in a #[link(wasm_import_module = ...)] statement
361+
/// The NAME to be used in a #[link(wasm_import_module = ...)] statement
315362
#[arg(long, value_name = "NAME")]
316363
wasm_import_module_name: Option<String>,
317-
/// Use dynamic loading mode with the given library name.
364+
/// Use dynamic loading mode with the given library NAME.
318365
#[arg(long, value_name = "NAME")]
319366
dynamic_loading: Option<String>,
320367
/// Require successful linkage to all functions in the library.
@@ -344,36 +391,36 @@ struct BindgenCommand {
344391
/// Deduplicates extern blocks.
345392
#[arg(long)]
346393
merge_extern_blocks: bool,
347-
/// Overrides the ABI of functions matching REGEX. The OVERRIDE value must be of the shape REGEX=ABI where ABI can be one of C, stdcall, efiapi, fastcall, thiscall, aapcs, win64 or C-unwind.
348-
#[arg(long, value_name = "OVERRIDE")]
349-
override_abi: Vec<String>,
394+
/// Overrides the ABI of functions matching REGEX. The OVERRIDE value must be of the shape REGEX=ABI where ABI can be one of C, stdcall, efiapi, fastcall, thiscall, aapcs, win64 or C-unwind<.>
395+
#[arg(long, value_name = "OVERRIDE", value_parser = parse_abi_override)]
396+
override_abi: Vec<(Abi, String)>,
350397
/// Wrap unsafe operations in unsafe blocks.
351398
#[arg(long)]
352399
wrap_unsafe_ops: bool,
353400
/// Derive custom traits on any kind of type. The CUSTOM value must be of the shape REGEX=DERIVE where DERIVE is a coma-separated list of derive macros.
354-
#[arg(long, value_name = "CUSTOM")]
355-
with_derive_custom: Vec<String>,
401+
#[arg(long, value_name = "CUSTOM", value_parser = parse_custom_derive)]
402+
with_derive_custom: Vec<(Vec<String>, String)>,
356403
/// Derive custom traits on a `struct`. The CUSTOM value must be of the shape REGEX=DERIVE where DERIVE is a coma-separated list of derive macros.
357-
#[arg(long, value_name = "CUSTOM")]
358-
with_derive_custom_struct: Vec<String>,
404+
#[arg(long, value_name = "CUSTOM", value_parser = parse_custom_derive)]
405+
with_derive_custom_struct: Vec<(Vec<String>, String)>,
359406
/// Derive custom traits on an `enum. The CUSTOM value must be of the shape REGEX=DERIVE where DERIVE is a coma-separated list of derive macros.
360-
#[arg(long, value_name = "CUSTOM")]
361-
with_derive_custom_enum: Vec<String>,
407+
#[arg(long, value_name = "CUSTOM", value_parser = parse_custom_derive)]
408+
with_derive_custom_enum: Vec<(Vec<String>, String)>,
362409
/// Derive custom traits on a `union`. The CUSTOM value must be of the shape REGEX=DERIVE where DERIVE is a coma-separated list of derive macros.
363-
#[arg(long, value_name = "CUSTOM")]
364-
with_derive_custom_union: Vec<String>,
410+
#[arg(long, value_name = "CUSTOM", value_parser = parse_custom_derive)]
411+
with_derive_custom_union: Vec<(Vec<String>, String)>,
365412
/// Generate wrappers for `static` and `static inline` functions.
366413
#[arg(long, requires = "experimental")]
367414
wrap_static_fns: bool,
368-
/// Sets the path for the source file that must be created due to the presence of `static` and
415+
/// Sets the PATH for the source file that must be created due to the presence of `static` and
369416
/// `static inline` functions.
370417
#[arg(long, requires = "experimental", value_name = "PATH")]
371418
wrap_static_fns_path: Option<PathBuf>,
372-
/// Sets the suffix added to the extern wrapper functions generated for `static` and `static
419+
/// Sets the SUFFIX added to the extern wrapper functions generated for `static` and `static
373420
/// inline` functions.
374421
#[arg(long, requires = "experimental", value_name = "SUFFIX")]
375422
wrap_static_fns_suffix: Option<String>,
376-
/// Set the default visibility of fields, including bitfields and accessor methods for
423+
/// Set the default VISIBILITY of fields, including bitfields and accessor methods for
377424
/// bitfields. This flag is ignored if the `--respect-cxx-access-specs` flag is used.
378425
#[arg(long, value_name = "VISIBILITY")]
379426
default_visibility: Option<FieldVisibilityKind>,
@@ -542,11 +589,7 @@ where
542589

543590
let mut builder = builder();
544591

545-
if let Some(header) = header {
546-
builder = builder.header(header);
547-
} else {
548-
return Err(Error::new(ErrorKind::Other, "Header not found"));
549-
}
592+
builder = builder.header(header);
550593

551594
if let Some(rust_target) = rust_target {
552595
builder = builder.rust_target(rust_target);
@@ -874,23 +917,7 @@ where
874917
builder = builder.formatter(formatter);
875918
}
876919

877-
if let Some(path_str) = rustfmt_configuration_file {
878-
let path = PathBuf::from(path_str);
879-
880-
if !path.is_absolute() {
881-
return Err(Error::new(
882-
ErrorKind::Other,
883-
"--rustfmt-configuration-file needs to be an absolute path!",
884-
));
885-
}
886-
887-
if path.to_str().is_none() {
888-
return Err(Error::new(
889-
ErrorKind::Other,
890-
"--rustfmt-configuration-file contains non-valid UTF8 characters.",
891-
));
892-
}
893-
920+
if let Some(path) = rustfmt_configuration_file {
894921
builder = builder.rustfmt_configuration_file(Some(path));
895922
}
896923

@@ -976,13 +1003,7 @@ where
9761003
builder = builder.merge_extern_blocks(true);
9771004
}
9781005

979-
for abi_override in override_abi {
980-
let (regex, abi_str) = abi_override
981-
.rsplit_once('=')
982-
.expect("Invalid ABI override: Missing `=`");
983-
let abi = abi_str
984-
.parse()
985-
.unwrap_or_else(|err| panic!("Invalid ABI override: {}", err));
1006+
for (abi, regex) in override_abi {
9861007
builder = builder.override_abi(abi, regex);
9871008
}
9881009

@@ -1052,12 +1073,7 @@ where
10521073
),
10531074
] {
10541075
let name = emit_diagnostics.then_some(name);
1055-
for custom_derive in custom_derives {
1056-
let (regex, derives) = custom_derive
1057-
.rsplit_once('=')
1058-
.expect("Invalid custom derive argument: Missing `=`");
1059-
let derives = derives.split(',').map(|s| s.to_owned()).collect();
1060-
1076+
for (derives, regex) in custom_derives {
10611077
let mut regex_set = RegexSet::new();
10621078
regex_set.insert(regex);
10631079
regex_set.build_with_diagnostics(false, name);

0 commit comments

Comments
 (0)