-
-
Notifications
You must be signed in to change notification settings - Fork 35
/
Copy pathvars.go
127 lines (114 loc) · 2.92 KB
/
vars.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
package runn
import (
"bytes"
"fmt"
"os"
"path/filepath"
"sort"
"strings"
"text/template"
"github.com/bmatcuk/doublestar/v4"
"github.com/goccy/go-json"
"github.com/goccy/go-yaml"
)
const multiple = "*"
type evaluator struct {
scheme string
exts []string
unmarshal func(data []byte, v any) error
}
var (
jsonEvaluator = &evaluator{scheme: "json://", exts: []string{"json"}, unmarshal: json.Unmarshal}
yamlEvaluator = &evaluator{scheme: "yaml://", exts: []string{"yml", "yaml"}, unmarshal: yaml.Unmarshal}
evaluators = []*evaluator{
jsonEvaluator,
yamlEvaluator,
}
)
func evaluateSchema(value any, operationRoot string, store map[string]any) (any, error) {
switch v := value.(type) {
case string:
var e *evaluator
for _, evaluator := range evaluators {
if strings.HasPrefix(v, evaluator.scheme) {
e = evaluator
}
}
if e == nil {
return value, nil
}
p := v[len(e.scheme):]
if strings.Contains(p, "://") {
return value, fmt.Errorf("invalid path: %s", v)
}
if !hasExts(p, e.exts) && !hasTemplateSuffix(p, e.exts) {
return value, fmt.Errorf("unsupported file extension: %s", p)
}
if !filepath.IsAbs(p) {
p = filepath.Join(operationRoot, p)
}
if strings.Contains(p, multiple) {
base, pattern := doublestar.SplitPattern(p)
fsys := os.DirFS(base)
matches, err := doublestar.Glob(fsys, pattern)
if err != nil {
return value, fmt.Errorf("glob error: %w", err)
}
sort.Slice(matches, func(i, j int) bool { return matches[i] < matches[j] })
var outs []any
for _, m := range matches {
out, err := evaluateFile(filepath.Join(base, m), store, e)
if err != nil {
return value, fmt.Errorf("evaluate file error: %w", err)
}
outs = append(outs, out)
}
return outs, nil
} else {
out, err := evaluateFile(p, store, e)
if err != nil {
return value, fmt.Errorf("evaluate file error: %w", err)
}
return out, nil
}
}
return value, nil
}
func hasExts(p string, exts []string) bool {
for _, ext := range exts {
if strings.HasSuffix(p, fmt.Sprintf(".%s", ext)) {
return true
}
}
return false
}
func hasTemplateSuffix(p string, exts []string) bool {
for _, ext := range exts {
if strings.HasSuffix(p, fmt.Sprintf(".%s.template", ext)) {
return true
}
}
return false
}
func evaluateFile(p string, store map[string]any, e *evaluator) (any, error) {
b, err := readFile(p)
if err != nil {
return nil, fmt.Errorf("read external files error: %w", err)
}
if store != nil && hasTemplateSuffix(p, e.exts) {
tmpl, err := template.New(p).Parse(string(b))
if err != nil {
return nil, fmt.Errorf("template parse error: %w", err)
}
buf := new(bytes.Buffer)
if err := tmpl.Execute(buf, store); err != nil {
return nil, fmt.Errorf("template excute error: %w", err)
}
b = buf.Bytes()
}
var out any
if err := e.unmarshal(b, &out); err != nil {
return nil, fmt.Errorf("unmarshal error: %w", err)
}
return out, nil
}