mirror of
https://github.com/fish-shell/fish-shell.git
synced 2025-02-17 10:12:46 +08:00
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:
parent
662a4740e2
commit
b65a53a2a6
|
@ -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
|
||||
|
|
99
fish-rust/src/builtins/command.rs
Normal file
99
fish-rust/src/builtins/command.rs
Normal 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
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -113,6 +113,7 @@ enum RustBuiltin : int32_t {
|
|||
Bg,
|
||||
Block,
|
||||
Contains,
|
||||
Command,
|
||||
Echo,
|
||||
Emit,
|
||||
Exit,
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
Loading…
Reference in New Issue
Block a user