-
Notifications
You must be signed in to change notification settings - Fork 0
/
ripoff_file.go
127 lines (112 loc) · 3.15 KB
/
ripoff_file.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 ripoff
import (
"bytes"
"fmt"
"os"
"path/filepath"
"regexp"
"strings"
"text/template"
"gopkg.in/yaml.v3"
)
type Row map[string]interface{}
type RipoffFile struct {
Rows map[string]Row `yaml:"rows"`
}
var funcMap = template.FuncMap{
// Convenient way to loop a set amount of times.
"intSlice": func(count int) ([]int, error) {
ret := make([]int, count)
for i := range ret {
ret[i] = i
}
return ret, nil
},
}
var templateFileRegex = regexp.MustCompile(`^template_(\S+)\.`)
// Adds newRows to existingRows, processing templated rows when needed.
func concatRows(templates *template.Template, existingRows map[string]Row, newRows map[string]Row, enums EnumValuesResult) error {
for rowId, row := range newRows {
_, rowExists := existingRows[rowId]
if rowExists {
return fmt.Errorf("row %s is defined more than once", rowId)
}
templateName, usesTemplate := row["template"].(string)
if usesTemplate {
// "rowId" allows dependencies between templated rows to be clear outside of the template.
// Templates can additionally use it to seed random generators.
templateVars := row
templateVars["rowId"] = rowId
templateVars["enums"] = enums
buf := &bytes.Buffer{}
err := templates.ExecuteTemplate(buf, templateName, templateVars)
if err != nil {
return err
}
ripoff := &RipoffFile{}
err = yaml.Unmarshal(buf.Bytes(), ripoff)
if err != nil {
return err
}
for templateRowId, templateRow := range ripoff.Rows {
_, rowExists := existingRows[templateRowId]
if rowExists {
return fmt.Errorf("row %s is defined more than once", rowId)
}
existingRows[templateRowId] = templateRow
}
} else {
existingRows[rowId] = row
}
}
return nil
}
// Builds a single RipoffFile from a directory of yaml files.
func RipoffFromDirectory(dir string, enums EnumValuesResult) (RipoffFile, error) {
dir = filepath.Clean(dir)
// Treat files starting with template_ as go templates.
templates := template.New("").Option("missingkey=error").Funcs(funcMap)
_, err := templates.ParseGlob(filepath.Join(dir, "template_*"))
if err != nil && !strings.Contains(err.Error(), "template: pattern matches no files") {
return RipoffFile{}, err
}
// Find all ripoff files in dir recursively.
allRipoffs := []RipoffFile{}
err = filepath.WalkDir(dir, func(path string, entry os.DirEntry, err error) error {
if err != nil {
return err
}
if filepath.Ext(path) != ".yaml" && filepath.Ext(path) != ".yml" {
return nil
}
// Templates were already processed.
templateNameMatches := templateFileRegex.FindStringSubmatch(entry.Name())
if len(templateNameMatches) == 2 {
return nil
}
yamlFile, err := os.ReadFile(path)
if err != nil {
return err
}
ripoff := &RipoffFile{}
err = yaml.Unmarshal(yamlFile, ripoff)
if err != nil {
return err
}
allRipoffs = append(allRipoffs, *ripoff)
return nil
})
if err != nil {
return RipoffFile{}, err
}
totalRipoff := RipoffFile{
Rows: map[string]Row{},
}
for _, ripoff := range allRipoffs {
err = concatRows(templates, totalRipoff.Rows, ripoff.Rows, enums)
if err != nil {
return RipoffFile{}, err
}
}
return totalRipoff, nil
}