Files
commandkit/parser.go

130 lines
4.0 KiB
Go

package commandkit
import (
"fmt"
"sort"
"strconv"
internalcobra "gitea.maximumdirect.net/eric/CommandKit/internal/cobra"
commandmeta "gitea.maximumdirect.net/eric/CommandKit/internal/commandmeta"
"gitea.maximumdirect.net/eric/CommandKit/internal/commandpath"
)
func (a *App) parse(args []string, options commandmeta.ParseOptions) commandmeta.Result {
if a == nil {
return commandmeta.Result{Failure: &commandmeta.Failure{Message: "cannot parse commands on a nil app"}}
}
result := internalcobra.Parse(a.routeSnapshot().metadata, args, options)
return reduceReportingOccurrences(result)
}
func reduceReportingOccurrences(result commandmeta.Result) commandmeta.Result {
selection := commandmeta.ReportingSelection{}
for index, occurrence := range result.FlagOccurrences {
if !commandmeta.IsReportingFlag(occurrence.Name) {
continue
}
enabled, err := strconv.ParseBool(occurrence.RawValue)
if err != nil {
result.FlagOccurrences = result.FlagOccurrences[:index+1]
result.Reporting = selection
result.Failure = &commandmeta.Failure{
Message: fmt.Sprintf("invalid boolean value %q for %s", occurrence.RawValue, occurrence.Spelling),
Usage: true,
}
return result
}
mode := commandmeta.ReportingModeNormal
switch occurrence.Name {
case "quiet":
mode = commandmeta.ReportingModeQuiet
case "verbose":
mode = commandmeta.ReportingModeVerbose
case "debug":
mode = commandmeta.ReportingModeDebug
case "progress":
selection.Progress = enabled
selection.ProgressSet = true
continue
}
if enabled {
selection.Mode = mode
} else if selection.Mode == mode {
selection.Mode = commandmeta.ReportingModeNormal
}
}
result.Reporting = selection
return result
}
func (a *App) routeSnapshot() *routingSnapshot {
if a == nil {
return nil
}
if a.snapshot == nil {
a.snapshot = a.buildRoutingSnapshot()
}
return a.snapshot
}
func (a *App) buildRoutingSnapshot() *routingSnapshot {
root := &commandmeta.Command{Name: a.identity.Name, CoreFlags: commandmeta.ReportingFlags()}
snapshot := &routingSnapshot{
metadata: root,
runners: make(map[commandmeta.Key]commandPayload),
aliases: a.aliases,
canonical: buildCanonicalTree(a.commands),
flagAliases: a.flagAliases,
discoveryFlags: a.discoveryFlags,
}
for _, rule := range a.routingRules {
if rule.record.kind != commandAliasRule {
continue
}
snapshot.commandAliases = append(snapshot.commandAliases, commandmeta.CommandAlias{
Source: []string(rule.record.sourcePath.Clone()),
Target: []string(snapshot.aliases.Resolve(rule.record.targetPath)),
})
}
sort.Slice(snapshot.commandAliases, func(i, j int) bool {
return commandpath.FromTokens(snapshot.commandAliases[i].Source).Key() < commandpath.FromTokens(snapshot.commandAliases[j].Source).Key()
})
names := make([]string, 0, len(a.commands))
for name := range a.commands {
names = append(names, name)
}
sort.Strings(names)
var key commandmeta.Key
for _, name := range names {
root.Children = append(root.Children, bridgeCommand(a.commands[name].command, snapshot, &key))
}
return snapshot
}
func bridgeCommand(command Command, snapshot *routingSnapshot, next *commandmeta.Key) *commandmeta.Command {
record := command.record
*next = *next + 1
declaration := &commandmeta.Command{
Key: *next,
Name: record.name,
Summary: record.summary,
Description: record.description,
CoreFlags: commandmeta.ReportingFlags(),
}
if record.payload != nil {
snapshot.runners[declaration.Key] = record.payload
declaration.Runnable = record.payload.hasRun()
declaration.Flags = record.payload.bridgeFlags()
declaration.Arguments = record.payload.bridgeArguments()
}
children := append([]Command(nil), record.children...)
sort.Slice(children, func(i, j int) bool { return children[i].record.name < children[j].record.name })
for _, child := range children {
if child.record == nil || child.record.absent {
continue
}
declaration.Children = append(declaration.Children, bridgeCommand(child, snapshot, next))
}
return declaration
}