From 71f5736d93a2ed7972c4b1ed9c9439de2110fea3 Mon Sep 17 00:00:00 2001 From: Antoine Stevan <44101798+amtoine@users.noreply.github.com> Date: Sun, 21 Jul 2024 13:58:34 +0200 Subject: [PATCH] RFC: add a command to help parsing arguments in scripts (#875) the other day, i was writing a script and wanted to pass a `list` to it which is not possible because there is no such things as _types_ for externals :thinking: i ended up writing an "arg parsing" command to help in that task and thought it could be useful to people :innocent: in this MR, i add `std-rfc parse-arg` in the `script-parsing.nu` module and add associated tests which all pass. i invite the reader to have a look at the docstring of `parse-arg` which should contain a full example explaining the usage of this new command :wink: --------- Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com> --- stdlib-candidate/std-rfc/mod.nu | 1 + stdlib-candidate/std-rfc/script-parsing.nu | 61 ++++++++++++++++++++++ stdlib-candidate/tests/mod.nu | 1 + stdlib-candidate/tests/script-parsing.nu | 37 +++++++++++++ 4 files changed, 100 insertions(+) create mode 100644 stdlib-candidate/std-rfc/script-parsing.nu create mode 100644 stdlib-candidate/tests/script-parsing.nu diff --git a/stdlib-candidate/std-rfc/mod.nu b/stdlib-candidate/std-rfc/mod.nu index 278a21e..7a5e469 100644 --- a/stdlib-candidate/std-rfc/mod.nu +++ b/stdlib-candidate/std-rfc/mod.nu @@ -6,3 +6,4 @@ export module math/ export use bulk-rename.nu * export use set-env.nu * export use bench.nu +export use script-parsing.nu [ parse-arg ] diff --git a/stdlib-candidate/std-rfc/script-parsing.nu b/stdlib-candidate/std-rfc/script-parsing.nu new file mode 100644 index 0000000..e9aa2c4 --- /dev/null +++ b/stdlib-candidate/std-rfc/script-parsing.nu @@ -0,0 +1,61 @@ +# helps parsing CLI arguments for Nushell scripts +# +# the following Nushell script does not make sense to be used as an external +# command because there is no such thing as a `list` in Bash for +# instance. +# ```nushell +# def main [x: list] { +# print $x +# } +# ``` +# +# one needs to write something less strict at parse-time and thus looses type +# information... +# ```nushell +# def main [ +# x: string, # list +# ] { +# print $x +# } +# ``` +# +# it's possible to write a much stronger script whith `parse-arg` +# ```nushell +# def main [ +# x: string, # list +# ] { +# let x = $x | parse-arg (metadata $x).span "list" # the script would crash if either +# # `$x: string` is not valid NUON or if +# # the resulting value is not a `list` +# print $x # here, `$x` is a `list` as intended +# } +# ``` +export def parse-arg [ + span: record, # the span of the input variable + expected_type: string, # the expected type for the input variable +]: [ string -> any ] { + let val = try { + $in | from nuon + } catch { + error make { + msg: $"(ansi red_bold)invalid NUON(ansi reset)", + label: { + text: "invalid NUON", + span: $span, + }, + } + } + + if ($val | describe) != $expected_type { + error make { + msg: $"(ansi red_bold)bad type(ansi reset)", + label: { + text: $"type: ($val | describe)", + span: $span, + }, + help: $"expected ($expected_type)", + } + } + + $val +} diff --git a/stdlib-candidate/tests/mod.nu b/stdlib-candidate/tests/mod.nu index 42fc3a4..8fdde90 100644 --- a/stdlib-candidate/tests/mod.nu +++ b/stdlib-candidate/tests/mod.nu @@ -3,4 +3,5 @@ export module record.nu export module str_xpend.nu export module math.nu export module bench.nu +export module script-parsing.nu export module str_dedent.nu diff --git a/stdlib-candidate/tests/script-parsing.nu b/stdlib-candidate/tests/script-parsing.nu new file mode 100644 index 0000000..e43e176 --- /dev/null +++ b/stdlib-candidate/tests/script-parsing.nu @@ -0,0 +1,37 @@ +use std assert +use ../std-rfc parse-arg + +const SPAN = { start: 0, end: 0 } + +export def "test parse-arg ok" [] { + const TEST_CASES = [ + [ input, type, expected ]; + + [ "123", "int", 123 ], + [ "[1, 2, 3]", "list", [1, 2, 3] ], + [ "'spam'", "string", "spam" ], + [ + "{ a: 1, b: 'egg', c: false }", + "record", + { a: 1, b: 'egg', c: false }, + ], + ] + + for t in $TEST_CASES { + assert equal ($t.input | parse-arg $SPAN $t.type) $t.expected + } +} + +export def "test parse-arg err" [] { + const TEST_CASES = [ + [ input, type ]; + + [ "{ invalid NUON", "" ], + [ "[1, 2, 3]", "string" ], + ] + + for t in $TEST_CASES { + let msg = $"test case: input: '($t.input)', type: ($t.type)" + assert error { $t.input | parse-arg $SPAN $t.type } $msg + } +}