blob: 1092dcd5d147a0c6c04327e8007be15ca2c20197 [file] [log] [blame]
David Tolnay39efd732020-08-30 13:21:51 -07001#[cfg(test)]
2#[path = "test.rs"]
3mod test;
4
David Tolnay39fa3662020-07-30 20:23:18 -07005use super::Opt;
6use clap::AppSettings;
7use std::ffi::{OsStr, OsString};
8use std::path::PathBuf;
9
10type App = clap::App<'static, 'static>;
11type Arg = clap::Arg<'static, 'static>;
12
13const USAGE: &str = "\
14 cxxbridge <input>.rs Emit .cc file for bridge to stdout
15 cxxbridge <input>.rs --header Emit .h file for bridge to stdout
16 cxxbridge --header Emit rust/cxx.h header to stdout\
17";
18
19const TEMPLATE: &str = "\
20{bin} {version}
21David Tolnay <dtolnay@gmail.com>
22https://github.com/dtolnay/cxx
23
24USAGE:
25 {usage}
26
27ARGS:
28{positionals}
29OPTIONS:
30{unified}\
31";
32
33fn app() -> App {
34 let mut app = App::new("cxxbridge")
35 .usage(USAGE)
36 .template(TEMPLATE)
37 .setting(AppSettings::NextLineHelp)
38 .arg(arg_input())
39 .arg(arg_cxx_impl_annotations())
40 .arg(arg_header())
41 .arg(arg_include())
42 .help_message("Print help information.")
43 .version_message("Print version information.");
44 if let Some(version) = option_env!("CARGO_PKG_VERSION") {
45 app = app.version(version);
46 }
47 app
48}
49
50const INPUT: &str = "input";
51const CXX_IMPL_ANNOTATIONS: &str = "cxx-impl-annotations";
52const HEADER: &str = "header";
53const INCLUDE: &str = "include";
54
55pub(super) fn from_args() -> Opt {
56 let matches = app().get_matches();
57 Opt {
58 input: matches.value_of_os(INPUT).map(PathBuf::from),
59 cxx_impl_annotations: matches.value_of(CXX_IMPL_ANNOTATIONS).map(str::to_owned),
60 header: matches.is_present(HEADER),
61 include: matches
62 .values_of(INCLUDE)
63 .map_or_else(Vec::new, |v| v.map(str::to_owned).collect()),
64 }
65}
66
67fn validate_utf8(arg: &OsStr) -> Result<(), OsString> {
68 if arg.to_str().is_some() {
69 Ok(())
70 } else {
71 Err(OsString::from("invalid utf-8 sequence"))
72 }
73}
74
75fn arg_input() -> Arg {
76 Arg::with_name(INPUT)
77 .help("Input Rust source file containing #[cxx::bridge].")
78 .required_unless(HEADER)
79}
80
81fn arg_cxx_impl_annotations() -> Arg {
82 const HELP: &str = "\
83Optional annotation for implementations of C++ function wrappers
84that may be exposed to Rust. You may for example need to provide
85__declspec(dllexport) or __attribute__((visibility(\"default\")))
86if Rust code from one shared object or executable depends on
87these C++ functions in another.
88 ";
89 Arg::with_name(CXX_IMPL_ANNOTATIONS)
90 .long(CXX_IMPL_ANNOTATIONS)
91 .takes_value(true)
92 .value_name("annotation")
93 .validator_os(validate_utf8)
94 .help(HELP)
95}
96
97fn arg_header() -> Arg {
98 Arg::with_name(HEADER)
99 .long(HEADER)
100 .help("Emit header with declarations only.")
101}
102
103fn arg_include() -> Arg {
104 const HELP: &str = "\
105Any additional headers to #include. The cxxbridge tool does not
106parse or even require the given paths to exist; they simply go
107into the generated C++ code as #include lines.
108 ";
109 Arg::with_name(INCLUDE)
110 .long(INCLUDE)
111 .short("i")
112 .takes_value(true)
113 .multiple(true)
114 .validator_os(validate_utf8)
115 .help(HELP)
116}