123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279 |
- package cli
- import (
- "errors"
- "fmt"
- "git.nspix.com/golang/micro/utils/console"
- "reflect"
- "sort"
- "strconv"
- "strings"
- "sync"
- "sync/atomic"
- "time"
- )
- var (
- _seq int64
- ErrInvalidHandle = errors.New("invalid handle kind")
- ErrInvalidHandleResponse = errors.New("invalid handle response")
- errInterface = reflect.TypeOf((*error)(nil)).Elem()
- stringSliceKind = reflect.TypeOf([]string{}).Kind()
- )
- type Executor struct {
- name string
- parent *Executor
- usage string
- description string
- handle interface{}
- children map[string]*Executor
- createdTime time.Time
- seq int64
- locker sync.RWMutex
- NotFoundHandle func(ctx *Context, cmd string) ([]byte, error)
- }
- type (
- executors []*Executor
- )
- func (p executors) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
- func (p executors) Len() int { return len(p) }
- func (p executors) Less(i, j int) bool { return p[i].seq < p[j].seq }
- func (exec *Executor) hasChildren(name string) bool {
- exec.locker.RLock()
- defer exec.locker.RUnlock()
- if name == "" {
- return len(exec.children) > 0
- }
- if _, ok := exec.children[name]; ok {
- return true
- }
- return false
- }
- func (exec *Executor) getDescription() string {
- if exec.description != "" {
- return exec.description
- }
- var p *Executor
- var text string
- p = exec
- for {
- if p.parent == nil {
- break
- }
- text = p.name + " " + text
- p = p.parent
- }
- return text
- }
- func (exec *Executor) Children(name string) (*Executor, error) {
- exec.locker.RLock()
- defer exec.locker.RUnlock()
- if cmd, ok := exec.children[name]; ok {
- return cmd, nil
- }
- return nil, errors.New("children not found")
- }
- func (exec *Executor) Completer(tokens ...string) []string {
- exec.locker.RLock()
- defer exec.locker.RUnlock()
- p := exec.children
- length := len(tokens)
- var token, prefix string
- result := make([]string, 0)
- if length > 0 {
- for i := 0; i < length; i++ {
- token = tokens[i]
- if i == length-1 {
- for k, e := range p {
- if token == k {
- if e.children != nil {
- for kk, _ := range e.children {
- result = append(result, prefix+token+" "+kk)
- }
- }
- } else if strings.HasPrefix(k, token) {
- result = append(result, prefix+k)
- }
- }
- } else {
- if next, ok := p[token]; !ok || next.children == nil {
- return []string{}
- } else {
- p = next.children
- }
- }
- prefix += token + " "
- }
- } else {
- for k, _ := range p {
- result = append(result, k)
- }
- }
- return result
- }
- func (exec *Executor) String() string {
- values := make([][]interface{}, 0)
- var loop func(string, *Executor)
- loop = func(prefix string, e *Executor) {
- if e.children == nil {
- return
- }
- vs := make(executors, len(e.children))
- var i int
- for _, v := range e.children {
- vs[i] = v
- i++
- }
- sort.Sort(vs)
- for _, v := range vs {
- if prefix == "" {
- values = append(values, []interface{}{v.name, v.getDescription()})
- } else {
- values = append(values, []interface{}{prefix + " " + v.name, v.getDescription()})
- }
- if prefix == "" {
- loop(v.name, v)
- } else {
- loop(prefix+" "+v.name, v)
- }
- }
- }
- loop("", exec)
- return string(console.Pretty(values, nil))
- }
- func (exec *Executor) Append(child ...*Executor) *Executor {
- exec.locker.Lock()
- defer exec.locker.Unlock()
- for _, v := range child {
- v.parent = exec
- exec.children[v.name] = v
- }
- return exec
- }
- func (exec *Executor) WithHandle(v interface{}) *Executor {
- exec.handle = v
- return exec
- }
- func (exec *Executor) Do(ctx *Context, args ...string) (b []byte, err error) {
- var (
- root interface{}
- )
- if root = ctx.Get("ROOT"); root == nil {
- ctx.Set("ROOT", exec)
- }
- ctx.Set("arguments", args)
- if len(args) > 0 && exec.hasChildren(args[0]) {
- if len(args) > 1 {
- return exec.children[args[0]].Do(ctx, args[1:]...)
- } else {
- return exec.children[args[0]].Do(ctx)
- }
- }
- if exec.handle == nil {
- if exec.NotFoundHandle != nil {
- b, err = exec.NotFoundHandle(ctx, ctx.CmdStr)
- } else {
- err = fmt.Errorf("%s not found", ctx.CmdStr)
- }
- return
- }
- refVal := reflect.ValueOf(exec.handle)
- refType := refVal.Type()
- if refType.Kind() != reflect.Func {
- err = ErrInvalidHandle
- return
- }
- //checking args
- if refType.NumIn() > 0 && len(args)+1 < refType.NumIn() {
- var usage string
- if exec.usage == "" {
- usage = "Usage: " + ctx.CmdStr + " ["
- for i := 1; i < refType.NumIn(); i++ {
- usage += " " + refType.In(i).String()
- }
- usage += " ]"
- } else {
- usage = "Usage: " + exec.usage
- }
- err = errors.New(usage)
- return
- }
- arguments := make([]reflect.Value, refType.NumIn())
- for i := 0; i < refType.NumIn(); i++ {
- if i == 0 {
- arguments[i] = reflect.ValueOf(ctx)
- continue
- }
- switch refType.In(i).Kind() {
- case reflect.String:
- arguments[i] = reflect.ValueOf(args[i-1])
- case reflect.Int:
- n, _ := strconv.ParseInt(args[i-1], 10, 64)
- arguments[i] = reflect.ValueOf(int(n))
- case reflect.Int32:
- n, _ := strconv.ParseInt(args[i-1], 10, 32)
- arguments[i] = reflect.ValueOf(int32(n))
- case reflect.Int64:
- n, _ := strconv.ParseInt(args[i-1], 10, 64)
- arguments[i] = reflect.ValueOf(n)
- case reflect.Float32:
- n, _ := strconv.ParseFloat(args[i-1], 32)
- arguments[i] = reflect.ValueOf(float32(n))
- case reflect.Float64:
- n, _ := strconv.ParseFloat(args[i-1], 64)
- arguments[i] = reflect.ValueOf(n)
- case stringSliceKind:
- arguments[i] = reflect.ValueOf(args[i-1:])
- default:
- err = fmt.Errorf("unsupported argument %d kind %s", i-1, refType.In(i).Kind().String())
- return
- }
- }
- values := refVal.Call(arguments)
- for _, v := range values {
- if v.Type().Implements(errInterface) {
- err = v.Interface().(error)
- } else if v.Kind() == reflect.Slice {
- b = v.Bytes()
- }
- }
- return
- }
- func NewExecutor(args ...string) *Executor {
- var (
- name string
- usage string
- description string
- )
- if len(args) > 0 {
- name = args[0]
- }
- if len(args) > 1 {
- usage = args[1]
- }
- if len(args) > 2 {
- description = args[2]
- }
- return &Executor{
- name: name,
- usage: usage,
- createdTime: time.Now(),
- description: description,
- children: make(map[string]*Executor),
- seq: atomic.AddInt64(&_seq, 1),
- }
- }
|