Reuse adapter sessions during execution
This commit is contained in:
119
execute.go
119
execute.go
@@ -19,34 +19,111 @@ func (a *App) Execute(ctx context.Context, args []string, streams Streams) Outco
|
||||
return a.present(Failure(errors.New("invocation context must not be nil")), nil, streams)
|
||||
}
|
||||
streams = normalizeStreams(streams)
|
||||
parsed := a.parse(args, internalcobra.ParseOptions{
|
||||
Context: ctx,
|
||||
Out: streams.Out,
|
||||
Err: streams.Err,
|
||||
})
|
||||
current := newExecution(a, ctx, streams)
|
||||
parsed := current.parse(args)
|
||||
if parsed.Failure != nil {
|
||||
return a.present(Usage(errors.New(parsed.Failure.Message)), parsed.Path, streams)
|
||||
return current.present(Usage(errors.New(parsed.Failure.Message)), parsed.Path)
|
||||
}
|
||||
command, ok := a.commandAt(parsed.Path)
|
||||
if !ok || command.payload == nil {
|
||||
return a.present(a.renderHelpOutcome(parsed.Path, streams), parsed.Path, streams)
|
||||
return current.present(current.renderHelp(parsed.Path), parsed.Path)
|
||||
}
|
||||
if parsed.HelpRequested || !command.payload.hasRun() {
|
||||
if _, err := command.payload.prepare(parsed.FlagValues, parsed.Operands, false); err != nil {
|
||||
return a.present(Usage(err), parsed.Path, streams)
|
||||
return current.present(Usage(err), parsed.Path)
|
||||
}
|
||||
return a.present(a.renderHelpOutcome(parsed.Path, streams), parsed.Path, streams)
|
||||
return current.present(current.renderHelp(parsed.Path), parsed.Path)
|
||||
}
|
||||
buildInput, err := command.payload.prepare(parsed.FlagValues, parsed.Operands, true)
|
||||
if err != nil {
|
||||
return a.present(Usage(err), parsed.Path, streams)
|
||||
return current.present(Usage(err), parsed.Path)
|
||||
}
|
||||
input := buildInput()
|
||||
if err := command.payload.checkInput(input); err != nil {
|
||||
return a.present(Usage(err), parsed.Path, streams)
|
||||
return current.present(Usage(err), parsed.Path)
|
||||
}
|
||||
if command.name == "help" {
|
||||
help, ok := input.(*helpInput)
|
||||
if !ok {
|
||||
return current.present(Failure(errors.New("built-in help input has an unexpected type")), parsed.Path)
|
||||
}
|
||||
return current.present(current.renderHelpTargets(help.Targets.Value), parsed.Path)
|
||||
}
|
||||
outcome := command.payload.run(Invocation{Context: ctx, Streams: streams}, input)
|
||||
return a.present(outcome, parsed.Path, streams)
|
||||
return current.present(outcome, parsed.Path)
|
||||
}
|
||||
|
||||
type execution struct {
|
||||
app *App
|
||||
ctx context.Context
|
||||
streams Streams
|
||||
options internalcobra.ParseOptions
|
||||
metadata *internalcobra.Command
|
||||
session *internalcobra.Session
|
||||
}
|
||||
|
||||
func newExecution(app *App, ctx context.Context, streams Streams) *execution {
|
||||
current := &execution{
|
||||
app: app,
|
||||
ctx: ctx,
|
||||
streams: streams,
|
||||
options: internalcobra.ParseOptions{Context: ctx, Out: streams.Out, Err: streams.Err},
|
||||
}
|
||||
if app != nil {
|
||||
current.metadata = app.bridgeTree()
|
||||
current.session = internalcobra.Prepare(current.metadata, current.options)
|
||||
}
|
||||
return current
|
||||
}
|
||||
|
||||
func (current *execution) parse(args []string) internalcobra.Result {
|
||||
if current.app == nil {
|
||||
return internalcobra.Result{Failure: &internalcobra.Failure{Message: "cannot parse commands on a nil app"}}
|
||||
}
|
||||
return current.session.Parse(args)
|
||||
}
|
||||
|
||||
func (current *execution) presentationSession() *internalcobra.Session {
|
||||
if current.app == nil {
|
||||
return nil
|
||||
}
|
||||
metadata := current.app.bridgeTree()
|
||||
if current.session == nil || current.session.Metadata() != metadata {
|
||||
current.metadata = metadata
|
||||
current.session = internalcobra.Prepare(metadata, current.options)
|
||||
}
|
||||
return current.session
|
||||
}
|
||||
|
||||
func (current *execution) renderHelp(path []string) Outcome {
|
||||
if current.app == nil {
|
||||
return Failure(errors.New("cannot render help on a nil app"))
|
||||
}
|
||||
return current.app.renderHelpOutcomeWithSession(path, current.streams, current.presentationSession())
|
||||
}
|
||||
|
||||
func (current *execution) renderHelpTargets(targets []string) Outcome {
|
||||
if current.app == nil {
|
||||
return Failure(errors.New("cannot render help on a nil app"))
|
||||
}
|
||||
return current.app.renderHelpTargetsWithSession(targets, current.streams, current.presentationSession())
|
||||
}
|
||||
|
||||
func (current *execution) usageLine(path []string) string {
|
||||
if current.app == nil {
|
||||
return "Usage: command [command]"
|
||||
}
|
||||
session := current.presentationSession()
|
||||
if session != nil {
|
||||
if usage, failure := session.UsageLine(path); failure == nil {
|
||||
return "Usage: " + usage
|
||||
}
|
||||
}
|
||||
return current.app.usageFallback()
|
||||
}
|
||||
|
||||
func (current *execution) present(outcome Outcome, path []string) Outcome {
|
||||
return current.app.presentWithUsageLine(outcome, path, current.streams, current.usageLine)
|
||||
}
|
||||
|
||||
func normalizeStreams(streams Streams) Streams {
|
||||
@@ -88,6 +165,10 @@ func (a *App) commandAt(path []string) (Command, bool) {
|
||||
}
|
||||
|
||||
func (a *App) present(outcome Outcome, path []string, streams Streams) Outcome {
|
||||
return a.presentWithUsageLine(outcome, path, streams, a.usageLine)
|
||||
}
|
||||
|
||||
func (a *App) presentWithUsageLine(outcome Outcome, path []string, streams Streams, usageLine func([]string) string) Outcome {
|
||||
if outcome.Kind == "" && outcome.Status == 0 && outcome.Domain == "" &&
|
||||
outcome.Cause == nil && outcome.DeliveryError == nil &&
|
||||
!outcome.ReportIncomplete && len(outcome.Diagnostics) == 0 {
|
||||
@@ -105,7 +186,7 @@ func (a *App) present(outcome Outcome, path []string, streams Streams) Outcome {
|
||||
diagnostics = append(diagnostics, MessageDiagnostic(configkit.SeverityError, outcome.Cause.Error()))
|
||||
}
|
||||
if outcome.Kind == OutcomeUsage {
|
||||
diagnostics = append(diagnostics, MessageDiagnostic(configkit.SeverityError, a.usageLine(path)))
|
||||
diagnostics = append(diagnostics, MessageDiagnostic(configkit.SeverityError, usageLine(path)))
|
||||
}
|
||||
outcome.Diagnostics = diagnostics
|
||||
if len(diagnostics) == 0 {
|
||||
@@ -137,15 +218,19 @@ func (a *App) present(outcome Outcome, path []string, streams Streams) Outcome {
|
||||
}
|
||||
|
||||
func (a *App) usageLine(path []string) string {
|
||||
name := "command"
|
||||
if a != nil && a.identity.Name != "" {
|
||||
name = a.identity.Name
|
||||
}
|
||||
if a != nil {
|
||||
if usage, failure := internalcobra.UsageLine(a.bridgeTree(), path); failure == nil {
|
||||
return "Usage: " + usage
|
||||
}
|
||||
}
|
||||
return a.usageFallback()
|
||||
}
|
||||
|
||||
func (a *App) usageFallback() string {
|
||||
name := "command"
|
||||
if a != nil && a.identity.Name != "" {
|
||||
name = a.identity.Name
|
||||
}
|
||||
return fmt.Sprintf("Usage: %s [command]", name)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user