Skip to content

Commit

Permalink
Add more options.
Browse files Browse the repository at this point in the history
  • Loading branch information
floitsch committed Nov 16, 2023
1 parent 90a32ad commit 1c0fe5d
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 0 deletions.
100 changes: 100 additions & 0 deletions src/cli.toit
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// Use of this source code is governed by an MIT-style license that can be
// found in the package's LICENSE file.
import uuid

import .parser_
import .utils_
import .help-generator_
Expand Down Expand Up @@ -533,6 +535,7 @@ class OptionEnum extends Option:
--split-commas=split-commas
if default and not values.contains default:
throw "Default value of '$name' is not a valid value: $default"

is-flag: return false

parse str/string --for-help-example/bool=false -> string:
Expand Down Expand Up @@ -594,6 +597,103 @@ class OptionInt extends Option:
return int.parse str --on-error=:
throw "Invalid integer value for option '$name': '$str'."

/**
An option for patterns.
Patterns are an extension to enums: they allow to specify a prefix to a value.
For example, a pattern `"interval:<duration>"` would allow values like
`"interval:1h"`, `"interval:30m"`, etc.
Both '=' and ':' are allowed as separators between the prefix and the value.
*/
class OptionPatterns extends Option:
default/string?
patterns/List
type/string

constructor name/string .patterns/List
--.default=null
--.type=(patterns.join "|")
--short-name/string?=null
--help/string?=null
--required/bool=false
--hidden/bool=false
--multi/bool=false
--split-commas/bool=false:
if multi and default: throw "Multi option can't have default value."
if required and default: throw "Option can't have default value and be required."
super.from-subclass name --short-name=short-name --help=help \
--required=required --hidden=hidden --multi=multi \
--split-commas=split-commas
if default:
parse_ default --on-error=:
throw "Default value of '$name' is not a valid value: $default"

is-flag -> bool: return false

/**
Returns the pattern that matches the given $str in a map with the pattern as key.
*/
parse str/string --for-help-example/bool=false -> any:
return parse_ str --on-error=:
throw "Invalid value for option '$name': '$str'. Valid values are: $(patterns.join ", ")."

parse_ str/string [--on-error]:
if not str.contains ":" and not str.contains "=":
if not patterns.contains str: on-error.call
return str

separator-index := str.index-of ":"
if separator-index < 0: separator-index = str.index-of "="
key := str[..separator-index]
key-with-equals := "$key="
key-with-colon := "$key:"
if not (patterns.any: it.starts-with key-with-equals or it.starts-with key-with-colon):
on-error.call

return {
key: str[separator-index + 1..]
}


/**
A Uuid option.
*/
class OptionUuid extends Option:
default/uuid.Uuid?

/**
Creates a new Uuid option.
The $default value is null.
The $type is set to 'uuid'.
Ensures that values are valid Uuids.
*/
constructor name/string
--.default=null
--short-name/string?=null
--help/string?=null
--required/bool=false
--hidden/bool=false
--multi/bool=false
--split-commas/bool=false:
if multi and default: throw "Multi option can't have default value."
if required and default: throw "Option can't have default value and be required."
super.from-subclass name --short-name=short-name --help=help \
--required=required --hidden=hidden --multi=multi \
--split-commas=split-commas

is-flag: return false

type -> string: return "uuid"

parse str/string --for-help-example/bool=false -> uuid.Uuid:
return uuid.parse str --on-error=:
throw "Invalid value for option '$name': '$str'. Expected a UUID."


/**
An option that must be a boolean value.
Expand Down
48 changes: 48 additions & 0 deletions tests/options_test.toit
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@
import cli
import expect show *
import uuid

main:
test-string
test-enum
test-patterns
test-int
test-uuid
test-flag
test-bad-combos

Expand Down Expand Up @@ -83,6 +86,30 @@ test-enum:
expect-throw "Invalid value for option 'enum': 'baz'. Valid values are: foo, bar.":
option.parse "baz"

test-patterns:
option := cli.OptionPatterns "pattern" ["foo", "bar:<duration>", "baz=<address>"]
expect-equals option.name "pattern"
expect-null option.default
expect-equals "foo|bar:<duration>|baz=<address>" option.type

option = cli.OptionPatterns "pattern" ["foo", "bar:<duration>", "baz=<address>"] --default="bar:1h"
expect-equals "bar:1h" option.default

value := option.parse "foo"
expect-equals "foo" value

value = option.parse "bar:1h"
expect-structural-equals { "bar": "1h" } value

value = option.parse "baz=neverland"
expect-structural-equals { "baz": "neverland" } value

expect-throw "Invalid value for option 'pattern': 'baz'. Valid values are: foo, bar:<duration>, baz=<address>.":
option.parse "baz"

expect-throw "Invalid value for option 'pattern': 'not-there'. Valid values are: foo, bar:<duration>, baz=<address>.":
option.parse "not-there"

test-int:
option := cli.OptionInt "int"
expect-equals option.name "int"
Expand All @@ -98,6 +125,27 @@ test-int:
expect-throw "Invalid integer value for option 'int': 'foo'.":
option.parse "foo"

test-uuid:
option := cli.OptionUuid "uuid"
expect-equals option.name "uuid"
expect-null option.default
expect-equals "uuid" option.type

option = cli.OptionUuid "uuid" --default=uuid.NIL
expect-equals uuid.NIL option.default

value := option.parse "00000000-0000-0000-0000-000000000000"
expect-equals uuid.NIL value

value = option.parse "00000000-0000-0000-0000-000000000001"
expect-equals (uuid.parse "00000000-0000-0000-0000-000000000001") value

expect-throw "Invalid value for option 'uuid': 'foo'. Expected a UUID.":
option.parse "foo"

expect-throw "Invalid value for option 'uuid': '00000000-0000-0000-0000-00000000000'. Expected a UUID.":
option.parse "00000000-0000-0000-0000-00000000000"

test-flag:
flag := cli.Flag "flag" --default=false
expect-equals flag.name "flag"
Expand Down

0 comments on commit 1c0fe5d

Please sign in to comment.