123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- package cli
- import (
- "errors"
- "fmt"
- "github.com/mattn/go-runewidth"
- "strconv"
- "strings"
- )
- var (
- ErrNotFound = errors.New("not found")
- )
- type Router struct {
- name string
- path []string
- children []*Router
- command Command
- params []string
- }
- func (r *Router) getChildren(name string) *Router {
- for _, child := range r.children {
- if child.name == name {
- return child
- }
- }
- return nil
- }
- func (r *Router) Completer(tokens ...string) []string {
- ss := make([]string, 0, 10)
- if len(tokens) == 0 {
- for _, child := range r.children {
- ss = append(ss, strings.Join(child.path, " "))
- }
- return ss
- }
- children := r.getChildren(tokens[0])
- if children == nil {
- token := tokens[0]
- for _, child := range r.children {
- if strings.HasPrefix(child.name, token) {
- ss = append(ss, strings.Join(child.path, " "))
- }
- }
- return ss
- }
- return children.Completer(tokens[1:]...)
- }
- func (r *Router) Usage() string {
- if len(r.path) <= 0 {
- return ""
- }
- var (
- sb strings.Builder
- )
- sb.WriteString("Usage: ")
- sb.WriteString(strings.Join(r.path, " "))
- if len(r.params) > 0 {
- for _, s := range r.params {
- sb.WriteString(" {" + s + "}")
- }
- }
- return sb.String()
- }
- func (r *Router) Handle(path string, command Command) {
- var (
- pos int
- name string
- )
- if strings.HasSuffix(path, "/") {
- path = strings.TrimSuffix(path, "/")
- }
- if strings.HasPrefix(path, "/") {
- path = strings.TrimPrefix(path, "/")
- }
- if path == "" {
- r.command = command
- return
- }
- if path[0] == ':' {
- ss := strings.Split(path, "/")
- for _, s := range ss {
- r.params = append(r.params, strings.TrimPrefix(s, ":"))
- }
- r.command = command
- return
- }
- if pos = strings.IndexByte(path, '/'); pos > -1 {
- name = path[:pos]
- path = path[pos:]
- } else {
- name = path
- path = ""
- }
- children := r.getChildren(name)
- if children == nil {
- children = newRouter(name)
- if len(r.path) == 0 {
- children.path = append(children.path, name)
- } else {
- children.path = append(children.path, r.path...)
- children.path = append(children.path, name)
- }
- r.children = append(r.children, children)
- }
- if children.command.Handle != nil {
- panic("a handle is already registered for path /" + strings.Join(children.path, "/"))
- }
- children.Handle(path, command)
- }
- func (r *Router) Lookup(tokens []string) (router *Router, args []string, err error) {
- if len(tokens) > 0 {
- children := r.getChildren(tokens[0])
- if children != nil {
- return children.Lookup(tokens[1:])
- }
- }
- if r.command.Handle == nil {
- err = ErrNotFound
- return
- }
- router = r
- args = tokens
- return
- }
- func (r *Router) String() string {
- var (
- sb strings.Builder
- width int
- maxWidth int
- walkFunc func(router *Router) []commander
- )
- walkFunc = func(router *Router) []commander {
- vs := make([]commander, 0, 5)
- if router.command.Handle != nil {
- vs = append(vs, commander{
- Name: router.name,
- Path: strings.Join(router.path, " "),
- Description: router.command.Description,
- })
- } else {
- if len(router.children) > 0 {
- for _, child := range router.children {
- vs = append(vs, walkFunc(child)...)
- }
- }
- }
- return vs
- }
- vs := walkFunc(r)
- for _, v := range vs {
- width = runewidth.StringWidth(v.Path)
- if width > maxWidth {
- maxWidth = width
- }
- }
- for _, v := range vs {
- sb.WriteString(fmt.Sprintf("%-"+strconv.Itoa(maxWidth+4)+"s %s\n", v.Path, v.Description))
- }
- return sb.String()
- }
- func newRouter(name string) *Router {
- return &Router{
- name: name,
- path: make([]string, 0, 4),
- params: make([]string, 0, 4),
- children: make([]*Router, 0, 10),
- }
- }
|