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 }