/
executor.go
137 lines (113 loc) · 3.53 KB
/
executor.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
package rules
import (
"fmt"
"log"
"os"
"github.com/aerogear/charmil/validator"
"github.com/imdario/mergo"
"github.com/spf13/cobra"
)
// Rules is an interface that is implemented
// by every rule defined in rules package
type Rules interface {
Validate() []validator.ValidationError
}
// RuleConfig is the struct that stores
// configuration of rules
type RuleConfig struct {
Verbose bool
Length
MustExist
}
// ExecuteRules executes all the rules
// according to the RuleConfig provided
func (config *RuleConfig) ExecuteRules(cmd *cobra.Command) []validator.ValidationError {
var errors []validator.ValidationError
info := validator.StatusLog{TotalTested: 0, TotalErrors: 0, Errors: errors}
// initialize default rules
config.initDefaultRules()
// validate the root command
config.validate(cmd, &info)
return config.executeHelper(cmd, &info)
}
func (config *RuleConfig) executeHelper(cmd *cobra.Command, info *validator.StatusLog) []validator.ValidationError {
info.Errors = config.executeRecursive(cmd, info)
// prints additional info for the checks
fmt.Fprintf(os.Stderr, "commands checked: %d\nchecks failed: %d\n", info.TotalTested, info.TotalErrors)
return info.Errors
}
// executeRecursive recursively traverse over all the subcommands
// and validate using executeRulesChildren function
func (config *RuleConfig) executeRecursive(cmd *cobra.Command, info *validator.StatusLog) []validator.ValidationError {
for _, child := range cmd.Commands() {
// base case
if !child.IsAvailableCommand() || child.IsAdditionalHelpTopicCommand() {
continue
}
// recursive call
info.Errors = config.executeRecursive(child, info)
}
info.Errors = config.executeRulesChildren(cmd, info)
return info.Errors
}
// executeRulesChildren execute rules on children of cmd
func (config *RuleConfig) executeRulesChildren(cmd *cobra.Command, info *validator.StatusLog) []validator.ValidationError {
children := cmd.Commands()
for _, child := range children {
if !child.IsAvailableCommand() || child.IsAdditionalHelpTopicCommand() {
continue
}
config.validate(child, info)
}
return info.Errors
}
// validate returns validation errors by executing the rules
func (config *RuleConfig) validate(cmd *cobra.Command, info *validator.StatusLog) {
rules := []Rules{
&LengthHelper{cmd: cmd, config: config},
&MustExistHelper{cmd: cmd, config: config},
}
// traverse all rules and validate
for _, rule := range rules {
validationErrors := rule.Validate()
info.TotalErrors += len(validationErrors)
info.Errors = append(info.Errors, validationErrors...)
info.TotalTested++
}
}
// initDefaultRules initialize default rules
// and overrides the default rules if RuleConfig is provided by the user
func (config *RuleConfig) initDefaultRules() {
// default config for rules
var defaultConfig = &RuleConfig{
Verbose: false,
Length: Length{
Limits: map[string]Limit{
"Use": {Min: 2},
"Short": {Min: 15},
"Long": {Min: 50},
"Example": {Min: 50},
},
},
MustExist: MustExist{
Fields: []string{"Use", "Short", "Long", "Example"},
},
}
if err := mergo.Merge(&config.Length, defaultConfig.Length); err != nil {
log.Fatal(err)
}
config.Fields = append(config.Fields, defaultConfig.Fields...)
config.Fields = removeDuplicates(config.Fields)
}
// remove duplicates from slice
func removeDuplicates(input []string) []string {
keys := make(map[string]bool)
list := []string{}
for _, entry := range input {
if _, value := keys[entry]; !value {
keys[entry] = true
list = append(list, entry)
}
}
return list
}