forked from lyraproj/dgoyaml
-
Notifications
You must be signed in to change notification settings - Fork 0
/
validate.go
137 lines (128 loc) · 3.32 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
package cli
import (
"flag"
"io/ioutil"
"os"
"strings"
"github.com/tada/catch"
"github.com/tada/catch/pio"
"github.com/tada/dgo/dgo"
"github.com/tada/dgo/tf"
"github.com/tada/dgo/util"
"github.com/tada/dgoyaml/yaml"
)
// Validate is the Dgo sub command that reads and validates YAML input using a spec written in
// YAML or Dgo
func Validate(parent Command) Command {
vc := &validateCommand{command: command{parent: parent, out: parent.Out(), err: parent.Err(), verbose: parent.Verbose()}}
flags := flag.NewFlagSet(`validate`, flag.ContinueOnError)
flags.StringVar(&vc.input, `input`, ``, `yaml file containing input to validate`)
flags.StringVar(&vc.spec, `spec`, ``, `yaml or dgo file with the parameter definitions`)
vc.flags = flags
return vc
}
type validateCommand struct {
command
input string
spec string
}
func readFileOrPanic(name string) []byte {
/* #nosec */
bs, err := ioutil.ReadFile(name)
if err != nil {
if os.IsNotExist(err) {
err = catch.Error(err)
}
panic(err)
}
return bs
}
func (h *validateCommand) run() int {
iMap := h.loadParameters(h.input)
sType := h.loadStructMapType(h.spec)
ok := true
if h.verbose {
bld := util.NewIndenter(` `)
ok = sType.(dgo.MapValidation).ValidateVerbose(iMap, bld)
pio.WriteString(h.out, bld.String())
} else {
vs := sType.(dgo.MapValidation).Validate(nil, iMap)
if len(vs) > 0 {
ok = false
for _, err := range vs {
pio.WriteString(h.out, err.Error())
pio.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(catch.Error(err))
}
var ok bool
iMap, ok = m.(dgo.Map)
if !ok {
panic(catch.Error(`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))
pio.WriteString(h.out, bld.String())
}
default:
panic(catch.Error(`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(catch.Error(err))
}
vMap, ok := m.(dgo.Map)
if !ok {
panic(catch.Error(`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(catch.Error(`file '%s' does not contain a struct definition`, spec))
}
default:
panic(catch.Error(`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 {
return h.RunWithCatch(func() int {
r, done := h.Parse(args)
if done {
return r
}
if h.input == `` {
return h.MissingOption(`input`)
}
if h.spec == `` {
return h.MissingOption(`spec`)
}
return h.run()
})
}