From 3b60bc1de0be62060fe09f226a9af53bb0fb2969 Mon Sep 17 00:00:00 2001 From: Neeraj Jaiswal Date: Mon, 20 Feb 2023 23:27:02 +0530 Subject: [PATCH] contains: port contains builtin to rust --- CMakeLists.txt | 2 +- fish-rust/src/builtins/contains.rs | 93 ++++++++++++++++++++++++++++++ fish-rust/src/builtins/mod.rs | 1 + fish-rust/src/builtins/shared.rs | 4 ++ src/builtin.cpp | 6 +- src/builtin.h | 1 + src/builtins/contains.cpp | 86 --------------------------- src/builtins/contains.h | 11 ---- 8 files changed, 104 insertions(+), 100 deletions(-) create mode 100644 fish-rust/src/builtins/contains.rs delete mode 100644 src/builtins/contains.cpp delete mode 100644 src/builtins/contains.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 21e2074d6..0b1dc2e5d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -102,7 +102,7 @@ set(FISH_BUILTIN_SRCS src/builtin.cpp src/builtins/abbr.cpp src/builtins/argparse.cpp src/builtins/bg.cpp src/builtins/bind.cpp src/builtins/block.cpp src/builtins/builtin.cpp src/builtins/cd.cpp src/builtins/command.cpp - src/builtins/commandline.cpp src/builtins/complete.cpp src/builtins/contains.cpp + src/builtins/commandline.cpp src/builtins/complete.cpp src/builtins/disown.cpp src/builtins/eval.cpp src/builtins/fg.cpp src/builtins/function.cpp src/builtins/functions.cpp src/builtins/history.cpp diff --git a/fish-rust/src/builtins/contains.rs b/fish-rust/src/builtins/contains.rs new file mode 100644 index 000000000..59600b628 --- /dev/null +++ b/fish-rust/src/builtins/contains.rs @@ -0,0 +1,93 @@ +// Implementation of the contains builtin. +use super::shared::{ + builtin_missing_argument, builtin_print_help, io_streams_t, STATUS_CMD_ERROR, STATUS_CMD_OK, + STATUS_INVALID_ARGS, +}; +use crate::builtins::shared::builtin_unknown_option; +use crate::ffi::parser_t; +use crate::wchar::{wstr, L}; +use crate::wgetopt::{wgetopter_t, wopt, woption, woption_argument_t}; +use crate::wutil::wgettext_fmt; +use libc::c_int; + +#[derive(Debug, Clone, Copy, Default)] +struct Options { + print_help: bool, + print_index: bool, +} + +fn parse_options( + args: &mut [&wstr], + parser: &mut parser_t, + streams: &mut io_streams_t, +) -> Result<(Options, usize), Option> { + let cmd = args[0]; + + const SHORT_OPTS: &wstr = L!("+:hi"); + const LONG_OPTS: &[woption] = &[ + wopt(L!("help"), woption_argument_t::no_argument, 'h'), + wopt(L!("index"), woption_argument_t::no_argument, 'i'), + ]; + + let mut opts = Options::default(); + + let mut w = wgetopter_t::new(SHORT_OPTS, LONG_OPTS, args); + while let Some(c) = w.wgetopt_long() { + match c { + 'h' => opts.print_help = true, + 'i' => opts.print_index = true, + ':' => { + builtin_missing_argument(parser, streams, cmd, args[w.woptind - 1], false); + return Err(STATUS_INVALID_ARGS); + } + '?' => { + builtin_unknown_option(parser, streams, cmd, args[w.woptind - 1], false); + return Err(STATUS_INVALID_ARGS); + } + _ => { + panic!("unexpected retval from wgetopt_long"); + } + } + } + + Ok((opts, w.woptind)) +} + +/// Implementation of the builtin contains command, used to check if a specified string is part of +/// a list. +pub fn contains( + parser: &mut parser_t, + streams: &mut io_streams_t, + args: &mut [&wstr], +) -> Option { + let cmd = args[0]; + + let (opts, optind) = match parse_options(args, parser, streams) { + Ok((opts, optind)) => (opts, optind), + Err(err @ Some(_)) if err != STATUS_CMD_OK => return err, + Err(err) => panic!("Illogical exit code from parse_options(): {err:?}"), + }; + + if opts.print_help { + builtin_print_help(parser, streams, cmd); + return STATUS_CMD_OK; + } + + let needle = args.get(optind); + if let Some(needle) = needle { + for (i, arg) in args[optind..].iter().enumerate().skip(1) { + if needle == arg { + if opts.print_index { + streams.out.append(wgettext_fmt!("%d\n", i)); + } + return STATUS_CMD_OK; + } + } + } else { + streams + .err + .append(wgettext_fmt!("%ls: Key not specified\n", cmd)); + } + + return STATUS_CMD_ERROR; +} diff --git a/fish-rust/src/builtins/mod.rs b/fish-rust/src/builtins/mod.rs index 16c4ca8cb..da78b3768 100644 --- a/fish-rust/src/builtins/mod.rs +++ b/fish-rust/src/builtins/mod.rs @@ -1,5 +1,6 @@ pub mod shared; +pub mod contains; pub mod echo; pub mod emit; pub mod exit; diff --git a/fish-rust/src/builtins/shared.rs b/fish-rust/src/builtins/shared.rs index b9dc55d14..3ac94b195 100644 --- a/fish-rust/src/builtins/shared.rs +++ b/fish-rust/src/builtins/shared.rs @@ -40,6 +40,9 @@ impl Vec {} /// A handy return value for successful builtins. pub const STATUS_CMD_OK: Option = Some(0); +/// The status code used for failure exit in a command (but not if the args were invalid). +pub const STATUS_CMD_ERROR: Option = Some(1); + /// A handy return value for invalid args. pub const STATUS_INVALID_ARGS: Option = Some(2); @@ -115,6 +118,7 @@ pub fn run_builtin( builtin: RustBuiltin, ) -> Option { match builtin { + RustBuiltin::Contains => super::contains::contains(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), diff --git a/src/builtin.cpp b/src/builtin.cpp index 4b73d1ef8..c19e14b36 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -39,7 +39,6 @@ #include "builtins/command.h" #include "builtins/commandline.h" #include "builtins/complete.h" -#include "builtins/contains.h" #include "builtins/disown.h" #include "builtins/eval.h" #include "builtins/fg.h" @@ -375,7 +374,7 @@ static constexpr builtin_data_t builtin_datas[] = { {L"command", &builtin_command, 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", &builtin_contains, N_(L"Search for a specified string in a list")}, + {L"contains", &implemented_in_rust, N_(L"Search for a specified string in a list")}, {L"continue", &builtin_break_continue, N_(L"Skip over remaining innermost loop")}, {L"count", &builtin_count, N_(L"Count the number of arguments")}, {L"disown", &builtin_disown, N_(L"Remove job from job list")}, @@ -524,6 +523,9 @@ const wchar_t *builtin_get_desc(const wcstring &name) { } static maybe_t try_get_rust_builtin(const wcstring &cmd) { + if (cmd == L"contains") { + return RustBuiltin::Contains; + } if (cmd == L"echo") { return RustBuiltin::Echo; } diff --git a/src/builtin.h b/src/builtin.h index dace4789e..7b74d40e3 100644 --- a/src/builtin.h +++ b/src/builtin.h @@ -109,6 +109,7 @@ int parse_help_only_cmd_opts(help_only_cmd_opts_t &opts, int *optind, int argc, /// An enum of the builtins implemented in Rust. enum RustBuiltin : int32_t { + Contains, Echo, Emit, Exit, diff --git a/src/builtins/contains.cpp b/src/builtins/contains.cpp deleted file mode 100644 index 1911ad452..000000000 --- a/src/builtins/contains.cpp +++ /dev/null @@ -1,86 +0,0 @@ -// Implementation of the contains builtin. -#include "config.h" // IWYU pragma: keep - -#include "contains.h" - -#include - -#include "../builtin.h" -#include "../common.h" -#include "../fallback.h" // IWYU pragma: keep -#include "../io.h" -#include "../maybe.h" -#include "../wgetopt.h" -#include "../wutil.h" // IWYU pragma: keep - -struct contains_cmd_opts_t { - bool print_help = false; - bool print_index = false; -}; -static const wchar_t *const short_options = L"+:hi"; -static const struct woption long_options[] = { - {L"help", no_argument, 'h'}, {L"index", no_argument, 'i'}, {}}; - -static int parse_cmd_opts(contains_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 'h': { - opts.print_help = true; - break; - } - case 'i': { - opts.print_index = 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 contains command, used to check if a specified string is part of -/// a list. -maybe_t builtin_contains(parser_t &parser, io_streams_t &streams, const wchar_t **argv) { - const wchar_t *cmd = argv[0]; - int argc = builtin_count_args(argv); - contains_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; - } - - const wchar_t *needle = argv[optind]; - if (!needle) { - streams.err.append_format(_(L"%ls: Key not specified\n"), cmd); - } else { - for (int i = optind + 1; i < argc; i++) { - if (!std::wcscmp(needle, argv[i])) { - if (opts.print_index) streams.out.append_format(L"%d\n", i - optind); - return STATUS_CMD_OK; - } - } - } - - return STATUS_CMD_ERROR; -} diff --git a/src/builtins/contains.h b/src/builtins/contains.h deleted file mode 100644 index ba6292b5c..000000000 --- a/src/builtins/contains.h +++ /dev/null @@ -1,11 +0,0 @@ -// Prototypes for executing builtin_contains function. -#ifndef FISH_BUILTIN_CONTAINS_H -#define FISH_BUILTIN_CONTAINS_H - -#include "../maybe.h" - -class parser_t; -struct io_streams_t; - -maybe_t builtin_contains(parser_t &parser, io_streams_t &streams, const wchar_t **argv); -#endif