forked from hashicorp/otto
-
Notifications
You must be signed in to change notification settings - Fork 0
/
router.go
138 lines (113 loc) · 3.36 KB
/
router.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package router
import (
"bytes"
"fmt"
"log"
"sort"
"strings"
"github.com/hashicorp/otto/ui"
)
// Router is a helper to route subcommands to specific callbacks.
//
// Actions are available on a lot of commands such as dev, deploy, etc. and
// this can be used to add custom actions.
type Router struct {
Actions map[string]Action
}
// Action defines an action that is available for the router.
type Action interface {
// Execute is the callback that'll be called to execute this action.
Execute(ctx Context) error
// Help is the help text for this action.
Help() string
// Synopsis is the text that will be shown as a short sentence
// about what this action does.
Synopsis() string
}
// Context is passed to the router and used to select which action is executed.
// This same value will also be passed down into the selected Action's Execute
// function. This is so that actions typecast the context to access
// implementation-specific data.
type Context interface {
RouteName() string
RouteArgs() []string
UI() ui.Ui
}
// Route will route the given Context to the proper Action.
func (r *Router) Route(ctx Context) error {
if _, ok := r.Actions["help"]; !ok {
r.Actions["help"] = &SimpleAction{
ExecuteFunc: r.help,
SynopsisText: "This help",
}
}
action, ok := r.Actions[ctx.RouteName()]
if !ok {
log.Printf("[DEBUG] No action found: %q; executing help.", ctx.RouteName())
return r.help(ctx)
}
return action.Execute(ctx)
}
func (r *Router) help(ctx Context) error {
badAction := false
var message bytes.Buffer
// If this is the help command we've been given a specific subcommand
// to look up, then do that.
if ctx.RouteName() == "help" && len(ctx.RouteArgs()) > 0 {
if a, ok := r.Actions[ctx.RouteArgs()[0]]; ok {
ctx.UI().Raw(a.Help())
return nil
}
message.WriteString(fmt.Sprintf(
"Unsupported action: %s\n\n", ctx.RouteArgs()[0]))
badAction = true
}
// Normal help output...
if ctx.RouteName() != "" && ctx.RouteName() != "help" {
message.WriteString(fmt.Sprintf(
"Unsupported action: %s\n\n", ctx.RouteName()))
badAction = true
}
message.WriteString(fmt.Sprintf(
"The available subcommands are shown below along with a\n" +
"brief description of what that command does. For more complete\n" +
"help, call the `help` subcommand with the name of the specific\n" +
"subcommand you want help for, such as `help foo`.\n\n" +
"The subcommand '(default)' is the blank subcommand. For this\n" +
"you don't specify any additional text.\n\n"))
longestName := len("(default)")
actionLines := make([]string, 0, len(r.Actions))
for n, _ := range r.Actions {
if len(n) > longestName {
longestName = len(n)
}
}
fmtStr := fmt.Sprintf(" %%%ds\t%%s\n", longestName)
for n, a := range r.Actions {
if n == "" {
n = "(default)"
}
actionLines = append(actionLines, fmt.Sprintf(fmtStr, n, a.Synopsis()))
}
sort.Strings(actionLines)
message.WriteString(strings.Join(actionLines, ""))
if !badAction {
ctx.UI().Raw(message.String())
return nil
}
return fmt.Errorf(message.String())
}
type SimpleAction struct {
ExecuteFunc func(Context) error
HelpText string
SynopsisText string
}
func (sa *SimpleAction) Execute(ctx Context) error {
return sa.ExecuteFunc(ctx)
}
func (sa *SimpleAction) Help() string {
return sa.HelpText
}
func (sa *SimpleAction) Synopsis() string {
return sa.SynopsisText
}