Rewrite "command" builtin in Rust

This is basically a subset of type, so we might as well.

To be clear this is `command -s` and friends, if you do `command grep` that's
handled as a keyword.

One issue here is that we can't get "one path or not" because I don't
know how to translate a maybe_t? Do we need to make it a shared_ptr instead?
This commit is contained in:
Fabian Boehm 2023-04-14 18:12:46 +02:00
parent 662a4740e2
commit b65a53a2a6
8 changed files with 107 additions and 126 deletions

View File

@ -100,7 +100,7 @@ endif()
# List of sources for builtin functions.
set(FISH_BUILTIN_SRCS
src/builtin.cpp src/builtins/argparse.cpp src/builtins/bind.cpp
src/builtins/builtin.cpp src/builtins/cd.cpp src/builtins/command.cpp
src/builtins/builtin.cpp src/builtins/cd.cpp
src/builtins/commandline.cpp src/builtins/complete.cpp
src/builtins/disown.cpp
src/builtins/eval.cpp src/builtins/fg.cpp

View File

@ -0,0 +1,99 @@
use libc::c_int;
use crate::builtins::shared::{
builtin_missing_argument, builtin_print_help, builtin_unknown_option, io_streams_t,
STATUS_CMD_OK, STATUS_CMD_UNKNOWN, STATUS_INVALID_ARGS,
};
use crate::ffi::parser_t;
use crate::ffi::path_get_paths_ffi;
use crate::wchar::{wstr, WString, L};
use crate::wchar_ffi::WCharFromFFI;
use crate::wchar_ffi::WCharToFFI;
use crate::wgetopt::{wgetopter_t, wopt, woption, woption_argument_t};
use crate::wutil::sprintf;
#[derive(Default)]
struct command_cmd_opts_t {
all: bool,
quiet: bool,
find_path: bool,
}
pub fn r#command(
parser: &mut parser_t,
streams: &mut io_streams_t,
argv: &mut [&wstr],
) -> Option<c_int> {
let cmd = argv[0];
let argc = argv.len();
let print_hints = false;
let mut opts: command_cmd_opts_t = Default::default();
const shortopts: &wstr = L!(":hasqv");
const longopts: &[woption] = &[
wopt(L!("help"), woption_argument_t::no_argument, 'h'),
wopt(L!("all"), woption_argument_t::no_argument, 'a'),
wopt(L!("query"), woption_argument_t::no_argument, 'q'),
wopt(L!("quiet"), woption_argument_t::no_argument, 'q'),
wopt(L!("search"), woption_argument_t::no_argument, 's'),
];
let mut w = wgetopter_t::new(shortopts, longopts, argv);
while let Some(c) = w.wgetopt_long() {
match c {
'a' => opts.all = true,
'q' => opts.quiet = true,
's' => opts.find_path = true,
// -s and -v are aliases
'v' => opts.find_path = true,
'h' => {
builtin_print_help(parser, streams, cmd);
return STATUS_CMD_OK;
}
':' => {
builtin_missing_argument(parser, streams, cmd, argv[w.woptind - 1], print_hints);
return STATUS_INVALID_ARGS;
}
'?' => {
builtin_unknown_option(parser, streams, cmd, argv[w.woptind - 1], print_hints);
return STATUS_INVALID_ARGS;
}
_ => {
panic!("unexpected retval from wgeopter.next()");
}
}
}
// Quiet implies find_path.
if !opts.find_path && !opts.all && !opts.quiet {
builtin_print_help(parser, streams, cmd);
return STATUS_INVALID_ARGS;
}
let mut res = false;
let optind = w.woptind;
for arg in argv.iter().take(argc).skip(optind) {
// TODO: This always gets all paths, and then skips a bunch.
// For the common case, we want to get just the one path.
// Port this over once path.cpp is.
let paths: Vec<WString> = path_get_paths_ffi(&arg.to_ffi(), parser).from_ffi();
for path in paths.iter() {
res = true;
if opts.quiet {
return STATUS_CMD_OK;
}
streams.out.append(sprintf!("%ls\n", path));
if !opts.all {
break;
}
}
}
if res {
STATUS_CMD_OK
} else {
STATUS_CMD_UNKNOWN
}
}

View File

@ -3,6 +3,7 @@ pub mod shared;
pub mod abbr;
pub mod bg;
pub mod block;
pub mod command;
pub mod contains;
pub mod echo;
pub mod emit;

View File

@ -148,6 +148,7 @@ pub fn run_builtin(
RustBuiltin::Bg => super::bg::bg(parser, streams, args),
RustBuiltin::Block => super::block::block(parser, streams, args),
RustBuiltin::Contains => super::contains::contains(parser, streams, args),
RustBuiltin::Command => super::command::command(parser, streams, args),
RustBuiltin::Echo => super::echo::echo(parser, streams, args),
RustBuiltin::Emit => super::emit::emit(parser, streams, args),
RustBuiltin::Exit => super::exit::exit(parser, streams, args),

View File

@ -33,7 +33,6 @@
#include "builtins/bind.h"
#include "builtins/builtin.h"
#include "builtins/cd.h"
#include "builtins/command.h"
#include "builtins/commandline.h"
#include "builtins/complete.h"
#include "builtins/disown.h"
@ -365,7 +364,7 @@ static constexpr builtin_data_t builtin_datas[] = {
{L"builtin", &builtin_builtin, N_(L"Run a builtin specifically")},
{L"case", &builtin_generic, N_(L"Block of code to run conditionally")},
{L"cd", &builtin_cd, N_(L"Change working directory")},
{L"command", &builtin_command, N_(L"Run a command specifically")},
{L"command", &implemented_in_rust, N_(L"Run a command specifically")},
{L"commandline", &builtin_commandline, N_(L"Set or get the commandline")},
{L"complete", &builtin_complete, N_(L"Edit command specific completions")},
{L"contains", &implemented_in_rust, N_(L"Search for a specified string in a list")},
@ -535,6 +534,9 @@ static maybe_t<RustBuiltin> try_get_rust_builtin(const wcstring &cmd) {
if (cmd == L"contains") {
return RustBuiltin::Contains;
}
if (cmd == L"command") {
return RustBuiltin::Command;
}
if (cmd == L"echo") {
return RustBuiltin::Echo;
}

View File

@ -113,6 +113,7 @@ enum RustBuiltin : int32_t {
Bg,
Block,
Contains,
Command,
Echo,
Emit,
Exit,

View File

@ -1,112 +0,0 @@
// Implementation of the command builtin.
#include "config.h" // IWYU pragma: keep
#include "command.h"
#include <string>
#include "../builtin.h"
#include "../common.h"
#include "../env.h"
#include "../fallback.h" // IWYU pragma: keep
#include "../io.h"
#include "../maybe.h"
#include "../parser.h"
#include "../path.h"
#include "../wgetopt.h"
#include "../wutil.h" // IWYU pragma: keep
struct command_cmd_opts_t {
bool print_help = false;
bool find_path = false;
bool quiet = false;
bool all_paths = false;
};
static const wchar_t *const short_options = L":ahqsv";
static const struct woption long_options[] = {
{L"help", no_argument, 'h'}, {L"all", no_argument, 'a'}, {L"quiet", no_argument, 'q'},
{L"query", no_argument, 'q'}, {L"search", no_argument, 's'}, {}};
static int parse_cmd_opts(command_cmd_opts_t &opts, int *optind, int argc, const wchar_t **argv,
parser_t &parser, io_streams_t &streams) {
const wchar_t *cmd = argv[0];
int opt;
wgetopter_t w;
while ((opt = w.wgetopt_long(argc, argv, short_options, long_options, nullptr)) != -1) {
switch (opt) {
case 'a': {
opts.all_paths = true;
break;
}
case 'h': {
opts.print_help = true;
break;
}
case 'q': {
opts.quiet = true;
break;
}
case 's': // -s and -v are aliases
case 'v': {
opts.find_path = true;
break;
}
case ':': {
builtin_missing_argument(parser, streams, cmd, argv[w.woptind - 1]);
return STATUS_INVALID_ARGS;
}
case '?': {
builtin_unknown_option(parser, streams, cmd, argv[w.woptind - 1]);
return STATUS_INVALID_ARGS;
}
default: {
DIE("unexpected retval from wgetopt_long");
}
}
}
*optind = w.woptind;
return STATUS_CMD_OK;
}
/// Implementation of the builtin 'command'. Actual command running is handled by the parser, this
/// just processes the flags.
maybe_t<int> builtin_command(parser_t &parser, io_streams_t &streams, const wchar_t **argv) {
const wchar_t *cmd = argv[0];
int argc = builtin_count_args(argv);
command_cmd_opts_t opts;
int optind;
int retval = parse_cmd_opts(opts, &optind, argc, argv, parser, streams);
if (retval != STATUS_CMD_OK) return retval;
if (opts.print_help) {
builtin_print_help(parser, streams, cmd);
return STATUS_CMD_OK;
}
// Quiet implies find_path.
if (!opts.find_path && !opts.all_paths && !opts.quiet) {
builtin_print_help(parser, streams, cmd);
return STATUS_INVALID_ARGS;
}
int found = 0;
for (int idx = optind; argv[idx]; ++idx) {
const wchar_t *command_name = argv[idx];
if (opts.all_paths) {
wcstring_list_t paths = path_get_paths(command_name, parser.vars());
for (const auto &path : paths) {
if (!opts.quiet) streams.out.append_format(L"%ls\n", path.c_str());
++found;
}
} else { // Either find_path explicitly or just quiet.
if (auto path = path_get_path(command_name, parser.vars())) {
if (!opts.quiet) streams.out.append_format(L"%ls\n", path->c_str());
++found;
}
}
}
return found ? STATUS_CMD_OK : STATUS_CMD_UNKNOWN;
}

View File

@ -1,11 +0,0 @@
// Prototypes for executing builtin_command function.
#ifndef FISH_BUILTIN_COMMAND_H
#define FISH_BUILTIN_COMMAND_H
#include "../maybe.h"
class parser_t;
struct io_streams_t;
maybe_t<int> builtin_command(parser_t &parser, io_streams_t &streams, const wchar_t **argv);
#endif