blob: 6c9f5944cf68e3656f7b05c219e1a2a6a0f9240c [file] [log] [blame]
// Copyright (C) 2016 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package app
import (
"flag"
"fmt"
"io"
"strings"
"android.googlesource.com/platform/tools/gpu/framework/log"
)
// Verb holds information about a runnable api command.
type Verb struct {
Name string // The name of the command
Run func(ctx log.Context, flags flag.FlagSet) error // the action for the command
ShortHelp string // Help for the purpose of the command
ShortUsage string // Help for how to use the command
Flags flag.FlagSet // The command line flags it accepts
verbs []*Verb
selected *Verb
}
var (
globalVerbs Verb
)
// Add adds a new verb to the supported set, it will panic if a
// duplicate name is encountered.
func (v *Verb) Add(child *Verb) {
if len(v.Filter(child.Name)) != 0 {
panic(fmt.Errorf("Duplicate verb name %s", child.Name))
}
v.verbs = append(v.verbs, child)
if v.Run == nil {
v.Run = v.run
}
}
// Filter returns the filtered list of verbs who's names match the specified prefix.
func (v *Verb) Filter(prefix string) (result []*Verb) {
for _, child := range v.verbs {
if strings.HasPrefix(child.Name, prefix) {
result = append(result, child)
}
}
return result
}
// Invoke runs a verb, handing it the command line arguments it should process.
func (v *Verb) Invoke(ctx log.Context, args []string) error {
if len(args) < 1 {
Usage(ctx, "Must supply a verb")
return nil
}
verb := args[0]
matches := v.Filter(verb)
switch len(matches) {
case 1:
v.selected = matches[0]
v.selected.Flags.Usage = v.Flags.Usage
v.selected.Flags.Parse(args[1:])
return v.selected.Run(ctx, v.selected.Flags)
case 0:
Usage(ctx, "Verb '%s' is unknown", verb)
default:
Usage(ctx, "Verb '%s' is ambiguous", verb)
}
return nil
}
func (v *Verb) run(ctx log.Context, flags flag.FlagSet) error {
return v.Invoke(ctx, flags.Args())
}
func (v *Verb) shorthelp(raw io.Writer) {
if v.ShortHelp != "" {
fmt.Fprintf(raw, "%s: %s", v.Name, v.ShortHelp)
fmt.Fprintln(raw)
}
if v.selected != nil {
v.selected.shorthelp(raw)
}
}
func (v *Verb) hasFlags() bool {
result := false
v.Flags.VisitAll(func(*flag.Flag) {
result = true
})
return result
}
func (v *Verb) usage(raw io.Writer) {
fmt.Fprintf(raw, " %s", v.Name)
if v.hasFlags() {
fmt.Fprintf(raw, " [%s-flags]", v.Name)
}
if v.selected != nil {
v.selected.usage(raw)
} else {
if v.ShortUsage != "" {
fmt.Fprintf(raw, " %s", v.ShortUsage)
} else {
if len(v.verbs) > 0 {
fmt.Fprint(raw, " verb [args]")
}
}
fmt.Fprintln(raw)
}
}
func (v *Verb) help(raw io.Writer) {
v.Flags.SetOutput(raw)
if v.hasFlags() {
fmt.Fprintf(raw, "%s-flags:", v.Name)
fmt.Fprintln(raw)
v.Flags.PrintDefaults()
}
if v.selected != nil {
v.selected.help(raw)
} else if len(v.verbs) > 0 {
fmt.Fprintf(raw, "%s verbs:", v.Name)
fmt.Fprintln(raw)
for _, child := range v.verbs {
fmt.Fprintf(raw, " %s : %s", child.Name, child.ShortHelp)
fmt.Fprintln(raw)
}
}
}
// AddVerb adds a new verb to the supported set, it will panic if a
// duplicate name is encountered.
func AddVerb(v *Verb) {
globalVerbs.Add(v)
}
// FilterVerbs returns the filtered list of verbs who's names match the specified
// prefix.
func FilterVerbs(prefix string) (result []*Verb) {
return globalVerbs.Filter(prefix)
}
// VerbMain is a task that can be handed to Run to invoke the verb handling system.
func VerbMain(ctx log.Context) error {
return globalVerbs.Invoke(ctx, flag.Args())
}
func verbMainPrepare() {
globalVerbs.Name = Name
globalVerbs.ShortHelp = ShortHelp
globalVerbs.ShortUsage = ShortUsage
globalVerbs.Flags = *flag.CommandLine
}