133d722a9Sopenharmony_ci#[cfg(test)]
233d722a9Sopenharmony_ci#[path = "test.rs"]
333d722a9Sopenharmony_cimod test;
433d722a9Sopenharmony_ci
533d722a9Sopenharmony_ciuse super::{Opt, Output};
633d722a9Sopenharmony_ciuse crate::cfg::{self, CfgValue};
733d722a9Sopenharmony_ciuse crate::gen::include::Include;
833d722a9Sopenharmony_ciuse crate::syntax::IncludeKind;
933d722a9Sopenharmony_ciuse clap::builder::{ArgAction, ValueParser};
1033d722a9Sopenharmony_ciuse clap::{Arg, Command};
1133d722a9Sopenharmony_ciuse std::collections::{BTreeMap as Map, BTreeSet as Set};
1233d722a9Sopenharmony_ciuse std::path::PathBuf;
1333d722a9Sopenharmony_ciuse std::process;
1433d722a9Sopenharmony_ciuse std::sync::{Arc, Mutex, PoisonError};
1533d722a9Sopenharmony_ciuse syn::parse::Parser;
1633d722a9Sopenharmony_ci
1733d722a9Sopenharmony_ciconst USAGE: &str = "\
1833d722a9Sopenharmony_ci    cxxbridge <input>.rs              Emit .cc file for bridge to stdout
1933d722a9Sopenharmony_ci    cxxbridge <input>.rs --header     Emit .h file for bridge to stdout
2033d722a9Sopenharmony_ci    cxxbridge --header                Emit \"rust/cxx.h\" header to stdout\
2133d722a9Sopenharmony_ci";
2233d722a9Sopenharmony_ci
2333d722a9Sopenharmony_ciconst TEMPLATE: &str = "\
2433d722a9Sopenharmony_ci{bin} {version}
2533d722a9Sopenharmony_ciDavid Tolnay <dtolnay@gmail.com>
2633d722a9Sopenharmony_cihttps://github.com/dtolnay/cxx
2733d722a9Sopenharmony_ci
2833d722a9Sopenharmony_ci{usage-heading}
2933d722a9Sopenharmony_ci    {usage}
3033d722a9Sopenharmony_ci
3133d722a9Sopenharmony_ci{all-args}\
3233d722a9Sopenharmony_ci";
3333d722a9Sopenharmony_ci
3433d722a9Sopenharmony_cifn app() -> Command {
3533d722a9Sopenharmony_ci    let mut app = Command::new("cxxbridge")
3633d722a9Sopenharmony_ci        .override_usage(USAGE)
3733d722a9Sopenharmony_ci        .help_template(TEMPLATE)
3833d722a9Sopenharmony_ci        .next_line_help(true)
3933d722a9Sopenharmony_ci        .disable_help_flag(true)
4033d722a9Sopenharmony_ci        .disable_version_flag(true)
4133d722a9Sopenharmony_ci        .arg(arg_input())
4233d722a9Sopenharmony_ci        .arg(arg_cfg())
4333d722a9Sopenharmony_ci        .arg(arg_cxx_impl_annotations())
4433d722a9Sopenharmony_ci        .arg(arg_header())
4533d722a9Sopenharmony_ci        .arg(arg_help())
4633d722a9Sopenharmony_ci        .arg(arg_include())
4733d722a9Sopenharmony_ci        .arg(arg_output());
4833d722a9Sopenharmony_ci    if let Some(version) = option_env!("CARGO_PKG_VERSION") {
4933d722a9Sopenharmony_ci        app = app.arg(arg_version()).version(version);
5033d722a9Sopenharmony_ci    }
5133d722a9Sopenharmony_ci    app
5233d722a9Sopenharmony_ci}
5333d722a9Sopenharmony_ci
5433d722a9Sopenharmony_ciconst INPUT: &str = "input";
5533d722a9Sopenharmony_ciconst CFG: &str = "cfg";
5633d722a9Sopenharmony_ciconst CXX_IMPL_ANNOTATIONS: &str = "cxx-impl-annotations";
5733d722a9Sopenharmony_ciconst HELP: &str = "help";
5833d722a9Sopenharmony_ciconst HEADER: &str = "header";
5933d722a9Sopenharmony_ciconst INCLUDE: &str = "include";
6033d722a9Sopenharmony_ciconst OUTPUT: &str = "output";
6133d722a9Sopenharmony_ciconst VERSION: &str = "version";
6233d722a9Sopenharmony_ci
6333d722a9Sopenharmony_cipub(super) fn from_args() -> Opt {
6433d722a9Sopenharmony_ci    let matches = app().get_matches();
6533d722a9Sopenharmony_ci
6633d722a9Sopenharmony_ci    if matches.get_flag(HELP) {
6733d722a9Sopenharmony_ci        let _ = app().print_long_help();
6833d722a9Sopenharmony_ci        process::exit(0);
6933d722a9Sopenharmony_ci    }
7033d722a9Sopenharmony_ci
7133d722a9Sopenharmony_ci    let input = matches.get_one::<PathBuf>(INPUT).cloned();
7233d722a9Sopenharmony_ci    let cxx_impl_annotations = matches
7333d722a9Sopenharmony_ci        .get_one::<String>(CXX_IMPL_ANNOTATIONS)
7433d722a9Sopenharmony_ci        .map(String::clone);
7533d722a9Sopenharmony_ci    let header = matches.get_flag(HEADER);
7633d722a9Sopenharmony_ci    let include = matches
7733d722a9Sopenharmony_ci        .get_many::<String>(INCLUDE)
7833d722a9Sopenharmony_ci        .unwrap_or_default()
7933d722a9Sopenharmony_ci        .map(|include| {
8033d722a9Sopenharmony_ci            if include.starts_with('<') && include.ends_with('>') {
8133d722a9Sopenharmony_ci                Include {
8233d722a9Sopenharmony_ci                    path: include[1..include.len() - 1].to_owned(),
8333d722a9Sopenharmony_ci                    kind: IncludeKind::Bracketed,
8433d722a9Sopenharmony_ci                }
8533d722a9Sopenharmony_ci            } else {
8633d722a9Sopenharmony_ci                Include {
8733d722a9Sopenharmony_ci                    path: include.to_owned(),
8833d722a9Sopenharmony_ci                    kind: IncludeKind::Quoted,
8933d722a9Sopenharmony_ci                }
9033d722a9Sopenharmony_ci            }
9133d722a9Sopenharmony_ci        })
9233d722a9Sopenharmony_ci        .collect();
9333d722a9Sopenharmony_ci
9433d722a9Sopenharmony_ci    let mut outputs = Vec::new();
9533d722a9Sopenharmony_ci    for path in matches.get_many::<PathBuf>(OUTPUT).unwrap_or_default() {
9633d722a9Sopenharmony_ci        outputs.push(if path.as_os_str() == "-" {
9733d722a9Sopenharmony_ci            Output::Stdout
9833d722a9Sopenharmony_ci        } else {
9933d722a9Sopenharmony_ci            Output::File(path.clone())
10033d722a9Sopenharmony_ci        });
10133d722a9Sopenharmony_ci    }
10233d722a9Sopenharmony_ci    if outputs.is_empty() {
10333d722a9Sopenharmony_ci        outputs.push(Output::Stdout);
10433d722a9Sopenharmony_ci    }
10533d722a9Sopenharmony_ci
10633d722a9Sopenharmony_ci    let mut cfg = Map::new();
10733d722a9Sopenharmony_ci    for arg in matches.get_many::<String>(CFG).unwrap_or_default() {
10833d722a9Sopenharmony_ci        let (name, value) = cfg::parse.parse_str(arg).unwrap();
10933d722a9Sopenharmony_ci        cfg.entry(name).or_insert_with(Set::new).insert(value);
11033d722a9Sopenharmony_ci    }
11133d722a9Sopenharmony_ci
11233d722a9Sopenharmony_ci    Opt {
11333d722a9Sopenharmony_ci        input,
11433d722a9Sopenharmony_ci        header,
11533d722a9Sopenharmony_ci        cxx_impl_annotations,
11633d722a9Sopenharmony_ci        include,
11733d722a9Sopenharmony_ci        outputs,
11833d722a9Sopenharmony_ci        cfg,
11933d722a9Sopenharmony_ci    }
12033d722a9Sopenharmony_ci}
12133d722a9Sopenharmony_ci
12233d722a9Sopenharmony_cifn arg_input() -> Arg {
12333d722a9Sopenharmony_ci    Arg::new(INPUT)
12433d722a9Sopenharmony_ci        .help("Input Rust source file containing #[cxx::bridge].")
12533d722a9Sopenharmony_ci        .required_unless_present_any(&[HEADER, HELP])
12633d722a9Sopenharmony_ci        .value_parser(ValueParser::path_buf())
12733d722a9Sopenharmony_ci}
12833d722a9Sopenharmony_ci
12933d722a9Sopenharmony_cifn arg_cfg() -> Arg {
13033d722a9Sopenharmony_ci    const HELP: &str = "\
13133d722a9Sopenharmony_ciCompilation configuration matching what will be used to build
13233d722a9Sopenharmony_cithe Rust side of the bridge.";
13333d722a9Sopenharmony_ci    let bool_cfgs = Arc::new(Mutex::new(Map::<String, bool>::new()));
13433d722a9Sopenharmony_ci    Arg::new(CFG)
13533d722a9Sopenharmony_ci        .long(CFG)
13633d722a9Sopenharmony_ci        .num_args(1)
13733d722a9Sopenharmony_ci        .value_name("name=\"value\" | name[=true] | name=false")
13833d722a9Sopenharmony_ci        .action(ArgAction::Append)
13933d722a9Sopenharmony_ci        .value_parser(move |arg: &str| match cfg::parse.parse_str(arg) {
14033d722a9Sopenharmony_ci            Ok((_, CfgValue::Str(_))) => Ok(arg.to_owned()),
14133d722a9Sopenharmony_ci            Ok((name, CfgValue::Bool(value))) => {
14233d722a9Sopenharmony_ci                let mut bool_cfgs = bool_cfgs.lock().unwrap_or_else(PoisonError::into_inner);
14333d722a9Sopenharmony_ci                if let Some(&prev) = bool_cfgs.get(&name) {
14433d722a9Sopenharmony_ci                    if prev != value {
14533d722a9Sopenharmony_ci                        return Err(format!("cannot have both {0}=false and {0}=true", name));
14633d722a9Sopenharmony_ci                    }
14733d722a9Sopenharmony_ci                }
14833d722a9Sopenharmony_ci                bool_cfgs.insert(name, value);
14933d722a9Sopenharmony_ci                Ok(arg.to_owned())
15033d722a9Sopenharmony_ci            }
15133d722a9Sopenharmony_ci            Err(_) => Err("expected name=\"value\", name=true, or name=false".to_owned()),
15233d722a9Sopenharmony_ci        })
15333d722a9Sopenharmony_ci        .help(HELP)
15433d722a9Sopenharmony_ci}
15533d722a9Sopenharmony_ci
15633d722a9Sopenharmony_cifn arg_cxx_impl_annotations() -> Arg {
15733d722a9Sopenharmony_ci    const HELP: &str = "\
15833d722a9Sopenharmony_ciOptional annotation for implementations of C++ function wrappers
15933d722a9Sopenharmony_cithat may be exposed to Rust. You may for example need to provide
16033d722a9Sopenharmony_ci__declspec(dllexport) or __attribute__((visibility(\"default\")))
16133d722a9Sopenharmony_ciif Rust code from one shared object or executable depends on
16233d722a9Sopenharmony_cithese C++ functions in another.";
16333d722a9Sopenharmony_ci    Arg::new(CXX_IMPL_ANNOTATIONS)
16433d722a9Sopenharmony_ci        .long(CXX_IMPL_ANNOTATIONS)
16533d722a9Sopenharmony_ci        .num_args(1)
16633d722a9Sopenharmony_ci        .value_name("annotation")
16733d722a9Sopenharmony_ci        .value_parser(ValueParser::string())
16833d722a9Sopenharmony_ci        .help(HELP)
16933d722a9Sopenharmony_ci}
17033d722a9Sopenharmony_ci
17133d722a9Sopenharmony_cifn arg_header() -> Arg {
17233d722a9Sopenharmony_ci    const HELP: &str = "\
17333d722a9Sopenharmony_ciEmit header with declarations only. Optional if using `-o` with
17433d722a9Sopenharmony_cia path ending in `.h`.";
17533d722a9Sopenharmony_ci    Arg::new(HEADER).long(HEADER).num_args(0).help(HELP)
17633d722a9Sopenharmony_ci}
17733d722a9Sopenharmony_ci
17833d722a9Sopenharmony_cifn arg_help() -> Arg {
17933d722a9Sopenharmony_ci    Arg::new(HELP)
18033d722a9Sopenharmony_ci        .long(HELP)
18133d722a9Sopenharmony_ci        .help("Print help information.")
18233d722a9Sopenharmony_ci        .num_args(0)
18333d722a9Sopenharmony_ci}
18433d722a9Sopenharmony_ci
18533d722a9Sopenharmony_cifn arg_include() -> Arg {
18633d722a9Sopenharmony_ci    const HELP: &str = "\
18733d722a9Sopenharmony_ciAny additional headers to #include. The cxxbridge tool does not
18833d722a9Sopenharmony_ciparse or even require the given paths to exist; they simply go
18933d722a9Sopenharmony_ciinto the generated C++ code as #include lines.";
19033d722a9Sopenharmony_ci    Arg::new(INCLUDE)
19133d722a9Sopenharmony_ci        .long(INCLUDE)
19233d722a9Sopenharmony_ci        .short('i')
19333d722a9Sopenharmony_ci        .num_args(1)
19433d722a9Sopenharmony_ci        .action(ArgAction::Append)
19533d722a9Sopenharmony_ci        .value_parser(ValueParser::string())
19633d722a9Sopenharmony_ci        .help(HELP)
19733d722a9Sopenharmony_ci}
19833d722a9Sopenharmony_ci
19933d722a9Sopenharmony_cifn arg_output() -> Arg {
20033d722a9Sopenharmony_ci    const HELP: &str = "\
20133d722a9Sopenharmony_ciPath of file to write as output. Output goes to stdout if -o is
20233d722a9Sopenharmony_cinot specified.";
20333d722a9Sopenharmony_ci    Arg::new(OUTPUT)
20433d722a9Sopenharmony_ci        .long(OUTPUT)
20533d722a9Sopenharmony_ci        .short('o')
20633d722a9Sopenharmony_ci        .num_args(1)
20733d722a9Sopenharmony_ci        .action(ArgAction::Append)
20833d722a9Sopenharmony_ci        .value_parser(ValueParser::path_buf())
20933d722a9Sopenharmony_ci        .help(HELP)
21033d722a9Sopenharmony_ci}
21133d722a9Sopenharmony_ci
21233d722a9Sopenharmony_cifn arg_version() -> Arg {
21333d722a9Sopenharmony_ci    Arg::new(VERSION)
21433d722a9Sopenharmony_ci        .long(VERSION)
21533d722a9Sopenharmony_ci        .help("Print version information.")
21633d722a9Sopenharmony_ci        .action(ArgAction::Version)
21733d722a9Sopenharmony_ci}
218