router.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. package cli
  2. import (
  3. "errors"
  4. "fmt"
  5. "github.com/mattn/go-runewidth"
  6. "strconv"
  7. "strings"
  8. )
  9. var (
  10. ErrNotFound = errors.New("not found")
  11. )
  12. type Router struct {
  13. name string
  14. path []string
  15. children []*Router
  16. command Command
  17. params []string
  18. }
  19. func (r *Router) getChildren(name string) *Router {
  20. for _, child := range r.children {
  21. if child.name == name {
  22. return child
  23. }
  24. }
  25. return nil
  26. }
  27. func (r *Router) Completer(tokens ...string) []string {
  28. ss := make([]string, 0, 10)
  29. if len(tokens) == 0 {
  30. for _, child := range r.children {
  31. ss = append(ss, strings.Join(child.path, " "))
  32. }
  33. return ss
  34. }
  35. children := r.getChildren(tokens[0])
  36. if children == nil {
  37. token := tokens[0]
  38. for _, child := range r.children {
  39. if strings.HasPrefix(child.name, token) {
  40. ss = append(ss, strings.Join(child.path, " "))
  41. }
  42. }
  43. return ss
  44. }
  45. return children.Completer(tokens[1:]...)
  46. }
  47. func (r *Router) Usage() string {
  48. if len(r.path) <= 0 {
  49. return ""
  50. }
  51. var (
  52. sb strings.Builder
  53. )
  54. sb.WriteString("Usage: ")
  55. sb.WriteString(strings.Join(r.path, " "))
  56. if len(r.params) > 0 {
  57. for _, s := range r.params {
  58. sb.WriteString(" {" + s + "}")
  59. }
  60. }
  61. return sb.String()
  62. }
  63. func (r *Router) Handle(path string, command Command) {
  64. var (
  65. pos int
  66. name string
  67. )
  68. if strings.HasSuffix(path, "/") {
  69. path = strings.TrimSuffix(path, "/")
  70. }
  71. if strings.HasPrefix(path, "/") {
  72. path = strings.TrimPrefix(path, "/")
  73. }
  74. if path == "" {
  75. r.command = command
  76. return
  77. }
  78. if path[0] == ':' {
  79. ss := strings.Split(path, "/")
  80. for _, s := range ss {
  81. r.params = append(r.params, strings.TrimPrefix(s, ":"))
  82. }
  83. r.command = command
  84. return
  85. }
  86. if pos = strings.IndexByte(path, '/'); pos > -1 {
  87. name = path[:pos]
  88. path = path[pos:]
  89. } else {
  90. name = path
  91. path = ""
  92. }
  93. if name == "-" {
  94. name = "app"
  95. }
  96. children := r.getChildren(name)
  97. if children == nil {
  98. children = newRouter(name)
  99. if len(r.path) == 0 {
  100. children.path = append(children.path, name)
  101. } else {
  102. children.path = append(children.path, r.path...)
  103. children.path = append(children.path, name)
  104. }
  105. r.children = append(r.children, children)
  106. }
  107. if children.command.Handle != nil {
  108. panic("a handle is already registered for path /" + strings.Join(children.path, "/"))
  109. }
  110. children.Handle(path, command)
  111. }
  112. func (r *Router) Lookup(tokens []string) (router *Router, args []string, err error) {
  113. if len(tokens) > 0 {
  114. children := r.getChildren(tokens[0])
  115. if children != nil {
  116. return children.Lookup(tokens[1:])
  117. }
  118. }
  119. if r.command.Handle == nil {
  120. err = ErrNotFound
  121. return
  122. }
  123. router = r
  124. args = tokens
  125. return
  126. }
  127. func (r *Router) String() string {
  128. var (
  129. sb strings.Builder
  130. width int
  131. maxWidth int
  132. walkFunc func(router *Router) []commander
  133. )
  134. walkFunc = func(router *Router) []commander {
  135. vs := make([]commander, 0, 5)
  136. if router.command.Handle != nil {
  137. vs = append(vs, commander{
  138. Name: router.name,
  139. Path: strings.Join(router.path, " "),
  140. Description: router.command.Description,
  141. })
  142. } else {
  143. if len(router.children) > 0 {
  144. for _, child := range router.children {
  145. vs = append(vs, walkFunc(child)...)
  146. }
  147. }
  148. }
  149. return vs
  150. }
  151. vs := walkFunc(r)
  152. for _, v := range vs {
  153. width = runewidth.StringWidth(v.Path)
  154. if width > maxWidth {
  155. maxWidth = width
  156. }
  157. }
  158. for _, v := range vs {
  159. sb.WriteString(fmt.Sprintf("%-"+strconv.Itoa(maxWidth+4)+"s %s\n", v.Path, v.Description))
  160. }
  161. return sb.String()
  162. }
  163. func newRouter(name string) *Router {
  164. return &Router{
  165. name: name,
  166. path: make([]string, 0, 4),
  167. params: make([]string, 0, 4),
  168. children: make([]*Router, 0, 10),
  169. }
  170. }