Skip to content

Commit

Permalink
Update artichoke CLI to clap v4
Browse files Browse the repository at this point in the history
https://epage.github.io/blog/2022/09/clap4/

`--help` output is changed, so update the ui-test snapshots.
  • Loading branch information
lopopolo committed Sep 29, 2022
1 parent 2db5303 commit 3eca7b9
Show file tree
Hide file tree
Showing 8 changed files with 209 additions and 223 deletions.
48 changes: 20 additions & 28 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Expand Up @@ -19,7 +19,7 @@ homepage.workspace = true
documentation.workspace = true

[dependencies]
clap = { version = "3.2.5", optional = true, default-features = false, features = ["std", "suggestions"] }
clap = { version = "4.0.2", optional = true }
# XXX: load-bearing unused dependency.
#
# `rustyline` improperly declares its minimum version on `log` as `0.4` despite
Expand Down
22 changes: 10 additions & 12 deletions README.md
Expand Up @@ -94,22 +94,20 @@ temporary workaround is to inject data into the interpreter with the

```console
$ artichoke --help
artichoke 0.1.0-pre.0
Artichoke is a Ruby made with Rust.

USAGE:
artichoke [OPTIONS] [ARGS]
Usage: artichoke [OPTIONS] [programfile] [arguments]...

ARGS:
<programfile>
<arguments>...
Arguments:
[programfile]
[arguments]...

OPTIONS:
--copyright print the copyright
-e <commands> one line of script. Several -e's allowed. Omit [programfile]
-h, --help Print help information
-V, --version Print version information
--with-fixture <fixture> file whose contents will be read into the `$fixture` global
Options:
--copyright print the copyright
-e <commands> one line of script. Several -e's allowed. Omit [programfile]
--with-fixture <fixture> file whose contents will be read into the `$fixture` global
-h, --help Print help information
-V, --version Print version information
```

## Design and Goals
Expand Down
157 changes: 3 additions & 154 deletions src/bin/artichoke.rs
Expand Up @@ -19,43 +19,16 @@
//! Artichoke does not yet support reading from the local file system. A
//! temporary workaround is to inject data into the interpreter with the
//! `--with-fixture` flag, which reads file contents into a `$fixture` global.
//!
//! ```console
//! $ cargo run -q --bin artichoke -- --help
//! artichoke 0.1.0-pre.0
//! Artichoke is a Ruby made with Rust.
//!
//! USAGE:
//! artichoke [OPTIONS] [ARGS]
//!
//! ARGS:
//! <programfile>
//! <arguments>...
//!
//! OPTIONS:
//! --copyright print the copyright
//! -e <commands> one line of script. Several -e's allowed. Omit [programfile]
//! -h, --help Print help information
//! -V, --version Print version information
//! --with-fixture <fixture> file whose contents will be read into the `$fixture` global
//! ```

use std::env;
use std::error;
use std::ffi::OsString;
use std::io::{self, Write};
use std::path::PathBuf;
use std::process;

use artichoke::ruby::{self, Args, ExecutionResult};
use clap::builder::ArgAction;
use clap::{Arg, ArgMatches, Command};
use artichoke::ruby::cli;
use artichoke::ruby::{self, ExecutionResult};
use termcolor::{ColorChoice, StandardStream, WriteColor};

type Result<T> = ::std::result::Result<T, Box<dyn error::Error>>;

fn main() {
let args = match parse_args() {
let args = match cli::parse_args() {
Ok(args) => args,
Err(err) => {
// Suppress all errors at this point (e.g. from a broken pipe) since
Expand All @@ -80,127 +53,3 @@ fn main() {
}
}
}

fn parse_args() -> Result<Args> {
let matches = clap_matches(env::args_os())?;

let commands = matches
.get_many::<OsString>("commands")
.into_iter()
.flat_map(|s| s.map(Clone::clone))
.collect::<Vec<_>>();
let mut args = Args::empty()
.with_copyright(*matches.get_one::<bool>("copyright").expect("defaulted by clap"))
.with_fixture(matches.get_one::<PathBuf>("fixture").cloned());

// If no `-e` arguments are given, the first positional argument is the
// `programfile`. All trailing arguments are ARGV to the script.
//
// If there are `-e` arguments given, there is no programfile and all
// positional arguments are ARGV to the inline script.
//
// ```console
// $ ruby -e 'puts ARGV.inspect' a b c
// ["a", "b", "c"]
// $ cat foo.rb
// puts ARGV.inspect
// $ ruby foo.rb a b c
// ["a", "b", "c"]
// $ ruby bar.rb a b c
// ruby: No such file or directory -- bar.rb (LoadError)
// ```
if commands.is_empty() {
if let Some(programfile) = matches.get_one::<PathBuf>("programfile").cloned() {
args = args.with_programfile(Some(programfile));
if let Some(argv) = matches.get_many::<OsString>("arguments") {
let ruby_program_argv = argv.map(Clone::clone).collect::<Vec<_>>();
args = args.with_argv(ruby_program_argv);
}
}
} else {
args = args.with_commands(commands);
if let Some(first_arg) = matches.get_one::<PathBuf>("programfile").cloned() {
if let Some(argv) = matches.get_many::<OsString>("arguments") {
let ruby_program_argv = [OsString::from(first_arg)]
.into_iter()
.chain(argv.map(Clone::clone))
.collect::<Vec<_>>();
args = args.with_argv(ruby_program_argv);
} else {
args = args.with_argv(vec![OsString::from(first_arg)]);
}
}
}

Ok(args)
}

fn command() -> Command<'static> {
Command::new("artichoke")
.about("Artichoke is a Ruby made with Rust.")
.version(env!("CARGO_PKG_VERSION"))
.arg(
Arg::new("copyright")
.long("copyright")
.action(ArgAction::SetTrue)
.help("print the copyright"),
)
.arg(
Arg::new("commands")
.short('e')
.action(ArgAction::Append)
.value_parser(clap::value_parser!(OsString))
.help(r"one line of script. Several -e's allowed. Omit [programfile]"),
)
.arg(
Arg::new("fixture")
.long("with-fixture")
.takes_value(true)
.value_parser(clap::value_parser!(PathBuf))
.help("file whose contents will be read into the `$fixture` global"),
)
.arg(Arg::new("programfile").value_parser(clap::value_parser!(PathBuf)))
.arg(
Arg::new("arguments")
.multiple_values(true)
.value_parser(clap::value_parser!(OsString)),
)
.trailing_var_arg(true)
}

// NOTE: This routine is plucked from `ripgrep` as of commit
// `9f924ee187d4c62aa6ebe4903d0cfc6507a5adb5`.
//
// `ripgrep` is licensed with the MIT License Copyright (c) 2015 Andrew Gallant.
//
// https://github.com/BurntSushi/ripgrep/blob/9f924ee187d4c62aa6ebe4903d0cfc6507a5adb5/LICENSE-MIT
//
// See https://github.com/artichoke/artichoke/issues/1301.

/// Returns a clap matches object if the given arguments parse successfully.
///
/// Otherwise, if an error occurred, then it is returned unless the error
/// corresponds to a `--help` or `--version` request. In which case, the
/// corresponding output is printed and the current process is exited
/// successfully.
fn clap_matches<I, T>(args: I) -> Result<ArgMatches>
where
I: IntoIterator<Item = T>,
T: Into<OsString> + Clone,
{
let err = match command().try_get_matches_from(args) {
Ok(matches) => return Ok(matches),
Err(err) => err,
};
if err.use_stderr() {
return Err(err.into());
}
// Explicitly ignore any error returned by write!. The most likely error
// at this point is a broken pipe error, in which case, we want to ignore
// it and exit quietly.
//
// (This is the point of this helper function. clap's functionality for
// doing this will panic on a broken pipe error.)
let _ignored = write!(io::stdout(), "{}", err);
process::exit(0);
}
2 changes: 2 additions & 0 deletions src/ruby.rs
Expand Up @@ -19,6 +19,8 @@ use crate::backtrace;
use crate::filename::INLINE_EVAL_SWITCH;
use crate::prelude::*;

pub mod cli;

/// Command line arguments for Artichoke `ruby` frontend.
#[derive(Default, Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Args {
Expand Down

0 comments on commit 3eca7b9

Please sign in to comment.