Compile typed command runners

This commit is contained in:
2026-10-11 01:21:52 +00:00
parent 4e0611bc13
commit 74f6bdeade
5 changed files with 69 additions and 42 deletions

View File

@@ -119,13 +119,12 @@ type commandPayload interface {
hasRun() bool
bridgeFlags() []internalcobra.Flag
bridgeArguments() []internalcobra.Argument
prepare([][]string, []string, bool) (func() any, error)
checkInput(any) error
run(Invocation, any) Outcome
prepare([][]string, []string, bool) (func(*execution) Outcome, error)
}
type typedCommand[I any] struct {
spec CommandSpec[I]
spec CommandSpec[I]
privateRun func(commandExecution, I) Outcome
}
func (c typedCommand[I]) validate() error {
@@ -133,7 +132,7 @@ func (c typedCommand[I]) validate() error {
}
func (c typedCommand[I]) hasRun() bool {
return c.spec.Run != nil
return c.spec.Run != nil || c.privateRun != nil
}
func (c typedCommand[I]) bridgeFlags() []internalcobra.Flag {
@@ -167,36 +166,37 @@ func (c typedCommand[I]) bridgeArguments() []internalcobra.Argument {
return arguments
}
func (c typedCommand[I]) prepare(flagValues [][]string, operands []string, enforceRequired bool) (func() any, error) {
func (c typedCommand[I]) prepare(flagValues [][]string, operands []string, enforceRequired bool) (func(*execution) Outcome, error) {
prepared, err := prepareInput(c.spec, flagValues, operands, enforceRequired)
if err != nil {
return nil, err
}
return func() any {
return func(current *execution) Outcome {
input := prepared.build()
return &input
if c.spec.CheckInput != nil {
if err := c.spec.CheckInput(input); err != nil {
return classifyInputError(err)
}
}
if c.privateRun != nil {
return c.privateRun(current.commandContext(), input)
}
return c.spec.Run(Invocation{Context: current.ctx, Streams: current.streams}, input)
}, nil
}
func (c typedCommand[I]) run(invocation Invocation, input any) Outcome {
return c.spec.Run(invocation, *input.(*I))
}
func (c typedCommand[I]) checkInput(input any) error {
if c.spec.CheckInput == nil {
return nil
}
return c.spec.CheckInput(*input.(*I))
}
// NewCommand captures a typed command declaration. Construction issues are
// retained and reported when the declaration is installed.
func NewCommand[I any](spec CommandSpec[I]) Command {
return captureCommand(spec, nil)
}
func captureCommand[I any](spec CommandSpec[I], privateRun func(commandExecution, I) Outcome) Command {
copySpec := spec
copySpec.Flags = append([]Flag[I](nil), spec.Flags...)
copySpec.Arguments = append([]Argument[I](nil), spec.Arguments...)
copySpec.Children = append([]Command(nil), spec.Children...)
payload := typedCommand[I]{spec: copySpec}
payload := typedCommand[I]{spec: copySpec, privateRun: privateRun}
record := &commandRecord{
name: spec.Name, summary: spec.Summary, description: spec.Description,
children: copySpec.Children, payload: payload,