-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathvalidate.go
172 lines (157 loc) · 3.83 KB
/
validate.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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
package cli
import (
"errors"
"fmt"
"io"
"io/ioutil"
"strings"
"github.com/lyraproj/dgo/dgo"
"github.com/lyraproj/dgo/tf"
"github.com/lyraproj/dgo/util"
"github.com/lyraproj/dgoyaml/yaml"
)
// Help prints the validate command help on stdout
func (h *validateCommand) Help() int {
util.WriteString(h.out, `validate input against the dgo type system
Usage:
dgo validate [flags]
Aliases:
v, val, valid
Flags:
-i, --input Relative or absolute path to a yaml file containing input to validate
-s, --spec Relative or absolute path to a yaml or dgo file with the parameter definitions
Global Flags:
-v, --verbose Be verbose in output
`)
return 0
}
// Validate is the Dgo sub command that reads and validates YAML input using a spec written in
// YAML or Dgo
func Validate(out, err io.Writer, debug bool) Command {
return &validateCommand{command{`dgo validate`, out, err, debug}}
}
type validateCommand struct {
command
}
func readFileOrPanic(name string) []byte {
/* #nosec */
bs, err := ioutil.ReadFile(name)
if err != nil {
panic(err)
}
return bs
}
func (h *validateCommand) run(input, spec string) int {
iMap := h.loadParameters(input)
sType := h.loadStructMapType(spec)
ok := true
if h.verbose {
bld := util.NewIndenter(` `)
ok = sType.ValidateVerbose(iMap, bld)
util.WriteString(h.out, bld.String())
} else {
vs := sType.Validate(nil, iMap)
if len(vs) > 0 {
ok = false
for _, err := range vs {
util.WriteString(h.out, err.Error())
util.WriteRune(h.out, '\n')
}
}
}
if ok {
return 0
}
return 1
}
func (h *validateCommand) loadParameters(input string) (iMap dgo.Map) {
switch {
case strings.HasSuffix(input, `.yaml`), strings.HasSuffix(input, `.json`):
data := readFileOrPanic(input)
m, err := yaml.Unmarshal(data)
if err != nil {
panic(err)
}
var ok bool
iMap, ok = m.(dgo.Map)
if !ok {
panic(errors.New(`expecting data to be a map`))
}
if h.verbose {
bld := util.NewIndenter(` `)
bld.Append(`Got input yaml with:`)
b2 := bld.Indent()
b2.NewLine()
b2.AppendIndented(string(data))
util.WriteString(h.out, bld.String())
}
default:
panic(fmt.Errorf(`invalid file name '%s', expected file name to end with .yaml or .json`, input))
}
return
}
func (h *validateCommand) loadStructMapType(spec string) (sType dgo.StructMapType) {
switch {
case strings.HasSuffix(spec, `.yaml`), strings.HasSuffix(spec, `.json`):
m, err := yaml.Unmarshal(readFileOrPanic(spec))
if err != nil {
panic(err)
}
vMap, ok := m.(dgo.Map)
if !ok {
panic(errors.New(`expecting data to be a map`))
}
sType = tf.StructMapFromMap(false, vMap)
case strings.HasSuffix(spec, `.dgo`):
tp := tf.ParseFile(nil, spec, string(readFileOrPanic(spec)))
if st, ok := tp.(dgo.StructMapType); ok {
sType = st
} else {
panic(fmt.Errorf(`file '%s' does not contain a struct definition`, spec))
}
default:
panic(fmt.Errorf(`invalid file name '%s', expected file name to end with .yaml, .json, or .dgo`, spec))
}
return
}
// Do parses the validate command line options and runs the validation
func (h *validateCommand) Do(args []string) int {
if len(args) == 0 {
return h.MissingOption(`--input`)
}
input := ``
spec := ``
for len(args) > 0 {
opt := args[0]
args = args[1:]
switch opt {
case `help`, `-?`, `--help`, `-help`:
return h.Help()
case `-i`, `--input`:
if len(args) == 0 {
return h.MissingOptionArgument(opt)
}
input = args[0]
args = args[1:]
case `-s`, `--spec`:
if len(args) == 0 {
return h.MissingOptionArgument(opt)
}
spec = args[0]
args = args[1:]
case `-v`, `--verbose`:
h.verbose = true
default:
return h.UnknownOption(opt)
}
}
if input == `` {
return h.MissingOption(`--input`)
}
if spec == `` {
return h.MissingOption(`--spec`)
}
return h.RunWithCatch(func() int {
return h.run(input, spec)
})
}