-
Notifications
You must be signed in to change notification settings - Fork 0
/
base.go
142 lines (116 loc) · 4.4 KB
/
base.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
139
140
141
142
package gcmd
import (
"fmt"
"strings"
)
// Base is our command base. It keeps track of all commands and their handlers
type Base struct {
CommandSymbol string
Commands map[string]Command
UnknownCommandMessage string
Store
}
// Store is a string->interface map for storing context data to be passed to handlers
type Store map[string]interface{}
// New is the function for creating a new command base. Please use this function instead of creating an instance of Base yourself!
//
// If you really must create a Base yourself, then make sure that CommandSymbol and UnknownCommandMessage are set, and that
// you initialize the Commands map yourself or else you will run into some issues!
func New(commandSymbol string) Base {
return Base{
CommandSymbol: commandSymbol,
UnknownCommandMessage: "Unknown command",
Commands: make(map[string]Command),
Store: make(map[string]interface{}),
}
}
// CommandExists checks if a command is registered. It returns true if it is, false if it isn't.
// CommandExists compares the Name fields of commands.
func (base *Base) CommandExists(command string) bool {
if _, ok := base.Commands[command]; ok {
return true
}
return false
}
// GetCommand retrieves a Command by it's name field from the command base.
//
// GetCommand returns a Command and a boolean. This boolean is true if a command was found, and false if
// a command could not be found with the provided name.
func (base *Base) GetCommand(command string) (Command, bool) {
if cmd, ok := base.Commands[command]; ok {
return cmd, ok
}
return Command{}, false
}
// Register takes in a command string and a command handler function (type Handler).
//
// CommandExists is used to check if the command is already registered or not. If the command is
// already registered, an error will be returned.
func (base *Base) Register(command Command) error {
if base.CommandExists(command.Name) {
return fmt.Errorf("A command with that name already exists")
}
base.Commands[command.Name] = command
return nil
}
// HandleCommand is called to run what is assumed to be a command.
//
// HandlerCommand returns a boolean and an error. The boolean is true if the input provided was structed as a proper command
// meaning that it was a string whose first character is equal to this base's CommandSymbol. If it was not a structured command
// the bool will be false and the returned error will be nil. In this case, you should assume the user was not trying to execute
// a command.
//
// If the user entered a properly structured command, but it was either an unknown command or if the input failed validation
// then the bool will be false and error will be set to an error.
//
// If the command was fully executed successfully, the bool will be true and error will be nil.
func (base *Base) HandleCommand(input string, store Store) (bool, error) {
input = strings.TrimSpace(input)
if len(input) < 1 {
return false, nil
}
// If the first char wasn't equal to base.CommandSymbol, we assume that the user was not trying to execute a command
// so we return false and do not set an error.
if input[0:1] != base.CommandSymbol {
return false, nil
}
// Now that we know this is structured as a command, we can strip away the CommandSymbol char.
input = input[1:]
// Split the input by spaces to get all provided arguments
args := strings.Split(input, " ")
// Check if this command exists and retrieve it if it does. If it doesn't exist, return base.UnknownCommandMessage
command, ok := base.GetCommand(args[0])
if !ok {
return false, fmt.Errorf(base.UnknownCommandMessage)
}
// Remove the command identifier from args
args = args[1:]
// Build middleware chain for execution
handler := command.applyMiddleware()
// If validation passed, then build context and run the command handler.
ctx := Context{
Args: args,
Store: base.Store,
Command: &command,
}
// Add keys from provided store into the context store
for key, val := range store {
ctx.Set(key, val)
}
// Start chain
if err := handler(ctx); err != nil {
return false, err
}
return true, nil
}
// Set stores an interface to the store map
func (base *Base) Set(key string, value interface{}) {
if base.Store == nil {
base.Store = make(map[string]interface{})
}
base.Store[key] = value
}
// Get retrieves an interface from the store map
func (base *Base) Get(key string) interface{} {
return base.Store[key]
}