1use std::env;
4use std::ffi::OsString;
5use std::iter;
6use std::path::PathBuf;
7use std::process;
8
9use clap::builder::ArgAction;
10use clap::{Arg, ArgMatches, Command};
11
12use super::Args;
13
14#[must_use]
20#[expect(clippy::missing_panics_doc, reason = "clap errors are not surfaced due to defaults")]
21pub fn parse_args() -> Args {
22 let matches = clap_matches(env::args_os());
23
24 let commands = matches
25 .get_many::<OsString>("commands")
26 .into_iter()
27 .flat_map(|s| s.map(Clone::clone))
28 .collect::<Vec<_>>();
29 let mut args = Args::empty()
30 .with_copyright(*matches.get_one::<bool>("copyright").expect("defaulted by clap"))
31 .with_fixture(matches.get_one::<PathBuf>("fixture").cloned());
32
33 if commands.is_empty() {
50 if let Some(programfile) = matches.get_one::<PathBuf>("programfile").cloned() {
51 args = args.with_programfile(Some(programfile));
52 if let Some(argv) = matches.get_many::<OsString>("arguments") {
53 let ruby_program_argv = argv.map(Clone::clone).collect::<Vec<_>>();
54 args = args.with_argv(ruby_program_argv);
55 }
56 }
57 } else {
58 args = args.with_commands(commands);
59 if let Some(first_arg) = matches.get_one::<PathBuf>("programfile").cloned() {
60 if let Some(argv) = matches.get_many::<OsString>("arguments") {
61 let ruby_program_argv = iter::once(OsString::from(first_arg))
62 .chain(argv.map(Clone::clone))
63 .collect::<Vec<_>>();
64 args = args.with_argv(ruby_program_argv);
65 } else {
66 args = args.with_argv(vec![OsString::from(first_arg)]);
67 }
68 }
69 }
70
71 args
72}
73
74#[must_use]
90fn clap_matches<I, T>(args: I) -> ArgMatches
91where
92 I: IntoIterator<Item = T>,
93 T: Into<OsString> + Clone,
94{
95 let err = match cli().try_get_matches_from(args) {
96 Ok(matches) => return matches,
97 Err(err) => err,
98 };
99 let _ignored = err.print();
103 process::exit(0);
104}
105
106#[must_use]
108pub fn cli() -> Command {
109 Command::new("artichoke")
110 .about("Artichoke is a Ruby made with Rust.")
111 .version(env!("CARGO_PKG_VERSION"))
112 .arg(
113 Arg::new("copyright")
114 .long("copyright")
115 .action(ArgAction::SetTrue)
116 .help("print the copyright"),
117 )
118 .arg(
119 Arg::new("commands")
120 .short('e')
121 .action(ArgAction::Append)
122 .value_parser(clap::value_parser!(OsString))
123 .help(r"one line of script. Several -e's allowed. Omit [programfile]"),
124 )
125 .arg(
126 Arg::new("fixture")
127 .long("with-fixture")
128 .value_parser(clap::value_parser!(PathBuf))
129 .help("file whose contents will be read into the `$fixture` global"),
130 )
131 .arg(Arg::new("programfile").value_parser(clap::value_parser!(PathBuf)))
132 .arg(
133 Arg::new("arguments")
134 .num_args(..)
135 .value_parser(clap::value_parser!(OsString))
136 .trailing_var_arg(true),
137 )
138}
139
140#[cfg(test)]
141mod tests {
142 use super::*;
143
144 #[test]
145 fn verify_cli() {
146 cli().debug_assert();
147 }
148}