-
Notifications
You must be signed in to change notification settings - Fork 0
/
generator.go
154 lines (134 loc) · 3.95 KB
/
generator.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
package crudgen
import (
"bytes"
"fmt"
"os"
"regexp"
"sort"
"strconv"
"strings"
)
// Executes the descriptions to create CRUD functions
func Run(dscs []CRUDDescription) {
for _, dsc := range dscs {
vals := buildValues(dsc)
generate(vals, dsc.SrcFilePath)
}
}
// Parse Tags to create a map of properties with values
func parseTag(tag string) map[string]string {
props := map[string]string{}
keyValRe, _ := regexp.Compile(`^([A-Za-z0-9]*)(='([A-Za-z0-9_ ]*)')?$`)
toks := strings.Split(tag, ",")
for _, tok := range toks {
grps := keyValRe.FindStringSubmatch(tok)
switch len(grps) {
case 2:
props[grps[1]] = ""
case 4:
props[grps[1]] = grps[3]
default:
panic(fmt.Errorf("unexpected token key value format in \"%s\"", tok))
}
}
return props
}
func getOrder(val string, ok bool) int {
if !ok {
return 0
} else if num, err := strconv.Atoi(val); err != nil {
panic(fmt.Errorf("failed to convert %s to int: %w", val, err))
} else {
return num
}
}
// Builds pipeline values for the template from the description (input)
// Inspects the fields and tags of the model type to extract data for constructing queries
func buildValues(dsc CRUDDescription) CRUDTemplateValues {
vals := CRUDTemplateValues{
Package: dsc.PackageName,
Imports: map[string]string{},
RepoTypeName: dsc.RepoType.Name(),
ModelTypeName: dsc.ModelType.String(),
InterfaceName: dsc.ModelType.Name() + "CRUD",
TableName: dsc.TableName,
PKeyFields: []Attrib{},
InsertFields: []Attrib{},
UpdateFields: []Attrib{},
}
for i := 0; i < dsc.ModelType.NumField(); i++ {
field := dsc.ModelType.Field(i)
props := parseTag(field.Tag.Get("gen"))
// Get column name that is the alias of the field in the db
column, ex := props["name"]
if !ex || column == "" {
column = strings.ToLower(field.Name)
}
// Get order that tells the order of apearance in parameter lists and placeholders
order := 0
if val, ok := props["order"]; ok {
if num, err := strconv.Atoi(val); err != nil {
panic(fmt.Errorf("failed to convert %s to int: %w", val, err))
} else {
order = num
}
}
atr := Attrib{
Name: field.Name,
Type: field.Type.String(),
Column: column,
Order: order,
}
// If the field is marked as pk, add it to privatek eys
if _, ok := props["pk"]; ok {
vals.PKeyFields = append(vals.PKeyFields, atr)
if field.Type.PkgPath() != "" {
vals.Imports[field.Type.PkgPath()] = field.Type.PkgPath()
}
}
// If the field is NOT omitted from update, add it to updates
if _, ok := props["omitup"]; !ok {
vals.UpdateFields = append(vals.UpdateFields, atr)
}
// If the field is NOT omitted from insert, add it to insert
if _, ok := props["omitin"]; !ok {
vals.InsertFields = append(vals.InsertFields, atr)
}
}
// Check PK Orders for correctness and uniqueness
sort.Sort(AttribList(vals.PKeyFields))
for i, e := range vals.PKeyFields {
if e.Order == 0 {
panic(fmt.Errorf("%s is a primary key and must have an order > 0", e.Name))
} else if i+1 != e.Order {
panic(fmt.Errorf("order of %s is not unique (%d)", e.Name, e.Order))
}
}
if len(vals.PKeyFields) == 0 {
panic(fmt.Errorf("%s model must have a primary key", dsc.ModelType.String()))
}
// Assign order to update and insert attribute lists
for i := 0; i < len(vals.InsertFields); i++ {
vals.InsertFields[i].Order = i + 1
}
for i := 0; i < len(vals.UpdateFields); i++ {
vals.UpdateFields[i].Order = i + 1 + len(vals.PKeyFields)
}
vals.Imports[dsc.ModelType.PkgPath()] = dsc.ModelType.PkgPath()
return vals
}
// Generates the source file
func generate(vals CRUDTemplateValues, filename string) {
buf := new(bytes.Buffer)
err := pkgTemplate.Execute(buf, vals)
if err != nil {
panic(fmt.Errorf("failed to execute template: %w", err))
} else {
if f, err := os.Create(filename); err != nil {
panic(fmt.Errorf("failed to open file with name \"%s\": %w", filename, err))
} else {
defer f.Close()
buf.WriteTo(f)
}
}
}