Files
commandkit/flag_values.go

90 lines
2.7 KiB
Go

package commandkit
import (
"errors"
"fmt"
)
// ErrFlagInput identifies a missing required captured flag selected for an input.
var ErrFlagInput = errors.New("required flag input is missing")
// ErrFlagWiring identifies an invalid captured binding or destination.
var ErrFlagWiring = errors.New("flag wiring is invalid")
// FlagValues stores invocation-local values prepared for captured flags.
type FlagValues[I any] struct {
values map[*bindingIdentity]preparedValue[I]
}
// CaptureFlags adapts flags declared for I into flags on Outer. The returned
// flags preserve the original identities and store prepared values in target.
func CaptureFlags[Outer, I any](flags []Flag[I], target func(*Outer) *FlagValues[I]) []Flag[Outer] {
captured := make([]Flag[Outer], len(flags))
for index, original := range flags {
inner := original
identity := newBindingIdentity()
binding := flagBinding[Outer]{
identity: identity,
meta: inner.binding.meta,
defaultText: inner.binding.defaultText,
repeated: inner.binding.repeated,
boolean: inner.binding.boolean,
}
if target != nil && inner.binding.identity != nil && inner.binding.valid && inner.binding.prepare != nil {
binding.valid = true
binding.prepare = func(raw []string) (preparedValue[Outer], error) {
prepared, err := inner.binding.prepare(raw)
if err != nil {
return preparedValue[Outer]{}, err
}
return preparedValue[Outer]{
identity: identity,
supplied: prepared.supplied,
apply: func(outer *Outer) {
store := target(outer)
if store == nil {
return
}
if store.values == nil {
store.values = make(map[*bindingIdentity]preparedValue[I])
}
store.values[inner.binding.identity] = prepared
},
}, nil
}
}
captured[index] = Flag[Outer]{binding: binding}
}
return captured
}
// ApplyTo applies selected captured flags to an initialized destination.
// Repeated core-owned values are copied for each application.
func (values FlagValues[I]) ApplyTo(target *I, selected ...Flag[I]) error {
if target == nil {
return fmt.Errorf("%w: destination is nil", ErrFlagWiring)
}
for _, flag := range selected {
binding := flag.binding
if binding.identity == nil {
return fmt.Errorf("%w: selected flag has no identity", ErrFlagWiring)
}
prepared, exists := values.values[binding.identity]
if !exists {
return fmt.Errorf("%w: flag --%s was not captured", ErrFlagWiring, binding.meta.Name)
}
if binding.meta.Required && !prepared.supplied {
return fmt.Errorf("flag --%s: %w", binding.meta.Name, ErrFlagInput)
}
prepared.apply(target)
}
return nil
}
func classifyInputError(err error) Outcome {
if errors.Is(err, ErrFlagWiring) {
return Failure(err)
}
return Usage(err)
}