diff --git a/stdlib-candidate/std-rfc/mod.nu b/stdlib-candidate/std-rfc/mod.nu index 278a21e24..7a5e46942 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 000000000..e9aa2c484 --- /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 42fc3a4b3..8fdde90a2 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 000000000..e43e176fa --- /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 + } +}