-
Notifications
You must be signed in to change notification settings - Fork 0
/
template_set.go
88 lines (82 loc) · 2.33 KB
/
template_set.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
package sqlnt
import (
"errors"
"fmt"
"reflect"
"strings"
)
const sqlTag = "sql"
// NewTemplateSet builds a set of templates for the given struct type T
//
// Fields of type sqlnt.NamedTemplate are created and set from the field tag 'sql'
//
// Example:
//
// type MyTemplateSet struct {
// Select sqlnt.NamedTemplate `sql:"SELECT * FROM foo WHERE col_a = :a"`
// Insert sqlnt.NamedTemplate `sql:"INSERT INTO foo (col_a, col_b, col_c) VALUES(:a, :b, :c)"`
// Delete sqlnt.NamedTemplate `sql:"DELETE FROM foo WHERE col_a = :a"`
// }
// set, err := sqlnt.NewTemplateSet[MyTemplateSet]()
//
// Note: If the overall field tag does not contain a 'sql' tag nor any other tags (i.e. there are no double-quotes in it)
// then the entire field tag value is used as the template - enabling the use of carriage returns to format the statement
//
// Example:
//
// type MyTemplateSet struct {
// Select sqlnt.NamedTemplate `SELECT *
// FROM foo
// WHERE col_a = :a`
// }
func NewTemplateSet[T any](options ...any) (*T, error) {
var chk T
if reflect.TypeOf(chk).Kind() != reflect.Struct {
return nil, errors.New("not a struct")
}
r := new(T)
if err := setTemplateFields(reflect.ValueOf(r).Elem(), options...); err != nil {
return nil, err
}
return r, nil
}
// MustCreateTemplateSet is the same as NewTemplateSet except that it panics on error
func MustCreateTemplateSet[T any](options ...any) *T {
r, err := NewTemplateSet[T](options...)
if err != nil {
panic(err)
}
return r
}
var ntt = reflect.TypeOf((*NamedTemplate)(nil)).Elem()
func setTemplateFields(rv reflect.Value, options ...any) error {
rvt := rv.Type()
for i := 0; i < rv.NumField(); i++ {
fld := rv.Field(i)
if ft := rvt.Field(i); ft.IsExported() {
if fld.Type() == ntt {
tag, ok := ft.Tag.Lookup(sqlTag)
if !ok {
if ft.Tag != "" && !strings.ContainsRune(string(ft.Tag), '"') {
tag = string(ft.Tag)
} else {
return fmt.Errorf("field '%s' does not have '%s' tag", ft.Name, sqlTag)
}
}
if tmp, err := NewNamedTemplate(tag, options...); err == nil {
fld.Set(reflect.ValueOf(tmp))
} else {
return err
}
} else if fld.Kind() == reflect.Struct {
sub := reflect.New(fld.Type()).Elem()
if err := setTemplateFields(sub, options...); err == nil {
fld.Set(sub)
} else {
return err
}
}
}
}
return nil
}