Preserve alias context in usage errors
This commit is contained in:
56
binding.go
56
binding.go
@@ -1,6 +1,7 @@
|
||||
package commandkit
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
@@ -22,10 +23,10 @@ func ValueFlag[I, V any](meta FlagMeta, defaultValue *V, parse func(string) (V,
|
||||
binding.prepare = func(values []string) (preparedValue[I], error) {
|
||||
value := fallback
|
||||
supplied := len(values) > 0
|
||||
for _, raw := range values {
|
||||
for index, raw := range values {
|
||||
parsed, err := parse(raw)
|
||||
if err != nil {
|
||||
return preparedValue[I]{}, err
|
||||
return preparedValue[I]{}, &flagOccurrenceError{index: index, cause: err}
|
||||
}
|
||||
value = parsed
|
||||
}
|
||||
@@ -47,10 +48,10 @@ func BoolFlag[I any](meta FlagMeta, defaultValue bool, set func(*I, Supplied[boo
|
||||
binding.prepare = func(values []string) (preparedValue[I], error) {
|
||||
value := defaultValue
|
||||
supplied := len(values) > 0
|
||||
for _, raw := range values {
|
||||
for index, raw := range values {
|
||||
parsed, err := strconv.ParseBool(raw)
|
||||
if err != nil {
|
||||
return preparedValue[I]{}, err
|
||||
return preparedValue[I]{}, &flagOccurrenceError{index: index, cause: err}
|
||||
}
|
||||
value = parsed
|
||||
}
|
||||
@@ -76,10 +77,10 @@ func RepeatFlag[I, V any](meta FlagMeta, defaults []V, parse func(string) (V, er
|
||||
supplied := len(values) > 0
|
||||
if supplied {
|
||||
parsedValues = make([]V, 0, len(values))
|
||||
for _, raw := range values {
|
||||
for index, raw := range values {
|
||||
parsed, err := parse(raw)
|
||||
if err != nil {
|
||||
return preparedValue[I]{}, err
|
||||
return preparedValue[I]{}, &flagOccurrenceError{index: index, cause: err}
|
||||
}
|
||||
parsedValues = append(parsedValues, parsed)
|
||||
}
|
||||
@@ -158,6 +159,28 @@ type preparedInput[I any] struct {
|
||||
values []preparedValue[I]
|
||||
}
|
||||
|
||||
type flagOccurrenceError struct {
|
||||
index int
|
||||
cause error
|
||||
}
|
||||
|
||||
func (err *flagOccurrenceError) Error() string { return err.cause.Error() }
|
||||
|
||||
func (err *flagOccurrenceError) Unwrap() error { return err.cause }
|
||||
|
||||
type flagConversionError struct {
|
||||
binding string
|
||||
spelling string
|
||||
occurrence int
|
||||
cause error
|
||||
}
|
||||
|
||||
func (err *flagConversionError) Error() string {
|
||||
return fmt.Sprintf("flag %s occurrence %d: %v", err.spelling, err.occurrence, err.cause)
|
||||
}
|
||||
|
||||
func (err *flagConversionError) Unwrap() error { return err.cause }
|
||||
|
||||
func (p preparedInput[I]) build() I {
|
||||
var input I
|
||||
if p.newInput != nil {
|
||||
@@ -173,15 +196,36 @@ func (p preparedInput[I]) build() I {
|
||||
// creates an input value. When enforceRequired is false, missing required flags
|
||||
// and operands are allowed so metadata help can validate supplied syntax.
|
||||
func prepareInput[I any](spec CommandSpec[I], flagValues [][]string, operands []string, enforceRequired bool) (preparedInput[I], error) {
|
||||
return prepareInputWithSpellings(spec, flagValues, nil, operands, enforceRequired)
|
||||
}
|
||||
|
||||
func prepareInputWithSpellings[I any](spec CommandSpec[I], flagValues, flagSpellings [][]string, operands []string, enforceRequired bool) (preparedInput[I], error) {
|
||||
if len(flagValues) != len(spec.Flags) {
|
||||
return preparedInput[I]{}, fmt.Errorf("received %d flag value sets for %d bindings", len(flagValues), len(spec.Flags))
|
||||
}
|
||||
if flagSpellings == nil {
|
||||
flagSpellings = make([][]string, len(spec.Flags))
|
||||
}
|
||||
if len(flagSpellings) != len(spec.Flags) {
|
||||
return preparedInput[I]{}, fmt.Errorf("received %d flag spelling sets for %d bindings", len(flagSpellings), len(spec.Flags))
|
||||
}
|
||||
preparedFlags := make([]preparedValue[I], len(spec.Flags))
|
||||
missingRequired := ""
|
||||
for i, flag := range spec.Flags {
|
||||
binding := flag.binding
|
||||
value, err := binding.prepare(flagValues[i])
|
||||
if err != nil {
|
||||
var occurrenceErr *flagOccurrenceError
|
||||
if errors.As(err, &occurrenceErr) {
|
||||
spelling := "--" + binding.meta.Name
|
||||
if occurrenceErr.index >= 0 && occurrenceErr.index < len(flagSpellings[i]) && flagSpellings[i][occurrenceErr.index] != "" {
|
||||
spelling = flagSpellings[i][occurrenceErr.index]
|
||||
}
|
||||
return preparedInput[I]{}, &flagConversionError{
|
||||
binding: binding.meta.Name, spelling: spelling,
|
||||
occurrence: occurrenceErr.index + 1, cause: occurrenceErr.cause,
|
||||
}
|
||||
}
|
||||
return preparedInput[I]{}, fmt.Errorf("flag --%s: %w", binding.meta.Name, err)
|
||||
}
|
||||
preparedFlags[i] = value
|
||||
|
||||
Reference in New Issue
Block a user