Skip to content

Commit a495822

Browse files
committed
Add [--if-error] block to Option.parse for clean error reporting.
Option.parse methods previously used `throw` for validation errors, which propagated uncaught through the parser. Now they take a required `[--if-error]` block. The parser provides a block that calls `fatal` (which shows help and calls `ui.abort`), giving clean error output. Also adds `InFile.check [--if-error]` and `InFile.check --ui/Ui` overloads for flexible error handling on file existence checks. BREAKING: Option.parse now requires an `--if-error` block argument.
1 parent 913d57b commit a495822

3 files changed

Lines changed: 75 additions & 54 deletions

File tree

src/cli.toit

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -754,10 +754,12 @@ abstract class Option:
754754
/**
755755
Parses the given $str and returns the parsed value.
756756
757+
Calls the $if-error block with an error message if parsing fails.
758+
757759
If $for-help-example is true, only performs validation that is valid for examples.
758760
For example, a FileOption would not check that the file exists.
759761
*/
760-
abstract parse str/string --for-help-example/bool=false -> any
762+
abstract parse str/string [--if-error] --for-help-example/bool=false -> any
761763

762764
/**
763765
Returns the default completion candidates for this option.
@@ -844,7 +846,7 @@ class OptionString extends Option:
844846

845847
options-for-completion -> List: return []
846848

847-
parse str/string --for-help-example/bool=false -> string:
849+
parse str/string [--if-error] --for-help-example/bool=false -> string:
848850
return str
849851

850852
/**
@@ -911,9 +913,9 @@ class OptionEnum extends Option:
911913

912914
options-for-completion -> List: return values
913915

914-
parse str/string --for-help-example/bool=false -> string:
916+
parse str/string [--if-error] --for-help-example/bool=false -> string:
915917
if not values.contains str:
916-
throw "Invalid value for option '$name': '$str'. Valid values are: $(values.join ", ")."
918+
return if-error.call "Invalid value for option '$name': '$str'. Valid values are: $(values.join ", ")."
917919
return str
918920

919921
/**
@@ -970,9 +972,9 @@ class OptionInt extends Option:
970972

971973
options-for-completion -> List: return []
972974

973-
parse str/string --for-help-example/bool=false -> int:
975+
parse str/string [--if-error] --for-help-example/bool=false -> int:
974976
return int.parse str --if-error=:
975-
throw "Invalid integer value for option '$name': '$str'."
977+
return if-error.call "Invalid integer value for option '$name': '$str'."
976978

977979
/**
978980
An option for patterns.
@@ -1014,9 +1016,9 @@ class OptionPatterns extends Option:
10141016
/**
10151017
Returns the pattern that matches the given $str in a map with the pattern as key.
10161018
*/
1017-
parse str/string --for-help-example/bool=false -> any:
1019+
parse str/string [--if-error] --for-help-example/bool=false -> any:
10181020
return parse_ str --if-error=:
1019-
throw "Invalid value for option '$name': '$str'. Valid values are: $(patterns.join ", ")."
1021+
return if-error.call "Invalid value for option '$name': '$str'. Valid values are: $(patterns.join ", ")."
10201022

10211023
parse_ str/string [--if-error]:
10221024
if not str.contains ":" and not str.contains "=":
@@ -1089,7 +1091,7 @@ class OptionPath extends Option:
10891091
if is-directory: return DIRECTIVE-DIRECTORY-COMPLETION_
10901092
return DIRECTIVE-FILE-COMPLETION_
10911093

1092-
parse str/string --for-help-example/bool=false -> string:
1094+
parse str/string [--if-error] --for-help-example/bool=false -> string:
10931095
return str
10941096

10951097
/**
@@ -1155,12 +1157,12 @@ class OptionInFile extends Option:
11551157

11561158
completion-directive -> int?: return DIRECTIVE-FILE-COMPLETION_
11571159

1158-
parse str/string --for-help-example/bool=false -> any:
1160+
parse str/string [--if-error] --for-help-example/bool=false -> any:
11591161
if allow-dash and str == "-":
11601162
return InFile.stdin_ --option-name=name
11611163
result := InFile.from-path_ str --option-name=name
11621164
if check-exists and not for-help-example:
1163-
result.check
1165+
result.check --if-error=if-error
11641166
return result
11651167

11661168
/**
@@ -1226,7 +1228,7 @@ class OptionOutFile extends Option:
12261228

12271229
completion-directive -> int?: return DIRECTIVE-FILE-COMPLETION_
12281230

1229-
parse str/string --for-help-example/bool=false -> any:
1231+
parse str/string [--if-error] --for-help-example/bool=false -> any:
12301232
if allow-dash and str == "-":
12311233
return OutFile.stdout_ --create-directories=create-directories --option-name=name
12321234
return OutFile.from-path_ str --create-directories=create-directories --option-name=name
@@ -1265,9 +1267,27 @@ class InFile:
12651267
Throws if the file does not exist. Does nothing for stdin.
12661268
*/
12671269
check -> none:
1270+
check --if-error=: throw it
1271+
1272+
/**
1273+
Checks that the file exists.
1274+
1275+
Calls the $if-error block with an error message if the file does not exist.
1276+
Does nothing for stdin.
1277+
*/
1278+
check [--if-error] -> none:
12681279
if is-stdin: return
12691280
if not file.is-file path:
1270-
throw "File not found for option '$option-name': '$path'."
1281+
if-error.call "File not found for option '$option-name': '$path'."
1282+
1283+
/**
1284+
Checks that the file exists.
1285+
1286+
Calls $Ui.abort with the error message if the file does not exist.
1287+
Does nothing for stdin.
1288+
*/
1289+
check --ui/Ui -> none:
1290+
check --if-error=: ui.abort it
12711291

12721292
/**
12731293
Opens the file (or stdin) for reading.
@@ -1411,9 +1431,9 @@ class OptionUuid extends Option:
14111431

14121432
type -> string: return "uuid"
14131433

1414-
parse str/string --for-help-example/bool=false -> Uuid:
1434+
parse str/string [--if-error] --for-help-example/bool=false -> Uuid:
14151435
return Uuid.parse str --if-error=:
1416-
throw "Invalid value for option '$name': '$str'. Expected a UUID."
1436+
return if-error.call "Invalid value for option '$name': '$str'. Expected a UUID."
14171437

14181438

14191439
/**
@@ -1472,10 +1492,10 @@ class Flag extends Option:
14721492

14731493
options-for-completion -> List: return ["true", "false"]
14741494

1475-
parse str/string --for-help-example/bool=false -> bool:
1495+
parse str/string [--if-error] --for-help-example/bool=false -> bool:
14761496
if str == "true": return true
14771497
if str == "false": return false
1478-
throw "Invalid value for boolean flag '$name': '$str'. Valid values are: true, false."
1498+
return if-error.call "Invalid value for boolean flag '$name': '$str'. Valid values are: true, false."
14791499

14801500
/**
14811501
An example.

src/parser_.toit

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,13 @@ class Parser_:
6363
add-option := : | option/Option argument/string |
6464
if option.is-multi:
6565
values := option.should-split-commas ? argument.split "," : [argument]
66-
parsed := values.map: option.parse it --for-help-example=for-help-example_
66+
parsed := values.map:
67+
option.parse it --if-error=(: | msg | fatal path msg) --for-help-example=for-help-example_
6768
options[option.name].add-all parsed
6869
else if seen-options.contains option.name:
6970
fatal path "Option was provided multiple times: $option.name"
7071
else:
71-
value := option.parse argument --for-help-example=for-help-example_
72+
value := option.parse argument --if-error=(: | msg | fatal path msg) --for-help-example=for-help-example_
7273
options[option.name] = value
7374

7475
seen-options.add option.name

0 commit comments

Comments
 (0)