services
This commit is contained in:
+141
@@ -0,0 +1,141 @@
|
||||
package dbr
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var NameMapping = camelCaseToSnakeCase
|
||||
|
||||
func isUpper(b byte) bool {
|
||||
return 'A' <= b && b <= 'Z'
|
||||
}
|
||||
|
||||
func isLower(b byte) bool {
|
||||
return 'a' <= b && b <= 'z'
|
||||
}
|
||||
|
||||
func isDigit(b byte) bool {
|
||||
return '0' <= b && b <= '9'
|
||||
}
|
||||
|
||||
func toLower(b byte) byte {
|
||||
if isUpper(b) {
|
||||
return b - 'A' + 'a'
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func camelCaseToSnakeCase(name string) string {
|
||||
var buf strings.Builder
|
||||
buf.Grow(len(name) * 2)
|
||||
|
||||
for i := 0; i < len(name); i++ {
|
||||
buf.WriteByte(toLower(name[i]))
|
||||
if i != len(name)-1 && isUpper(name[i+1]) &&
|
||||
(isLower(name[i]) || isDigit(name[i]) ||
|
||||
(i != len(name)-2 && isLower(name[i+2]))) {
|
||||
buf.WriteByte('_')
|
||||
}
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
var (
|
||||
typeValuer = reflect.TypeOf((*driver.Valuer)(nil)).Elem()
|
||||
)
|
||||
|
||||
type tagStore struct {
|
||||
m map[reflect.Type][]string
|
||||
}
|
||||
|
||||
func newTagStore() *tagStore {
|
||||
return &tagStore{
|
||||
m: make(map[reflect.Type][]string),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *tagStore) get(t reflect.Type) []string {
|
||||
if t.Kind() != reflect.Struct {
|
||||
return nil
|
||||
}
|
||||
if _, ok := s.m[t]; !ok {
|
||||
l := make([]string, t.NumField())
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
if field.PkgPath != "" && !field.Anonymous {
|
||||
// unexported
|
||||
continue
|
||||
}
|
||||
tag := field.Tag.Get("db")
|
||||
if tag == "-" {
|
||||
// ignore
|
||||
continue
|
||||
}
|
||||
if tag == "" {
|
||||
// no tag, but we can record the field name
|
||||
tag = NameMapping(field.Name)
|
||||
}
|
||||
l[i] = tag
|
||||
}
|
||||
s.m[t] = l
|
||||
}
|
||||
return s.m[t]
|
||||
}
|
||||
|
||||
func (s *tagStore) findPtr(value reflect.Value, name []string, ptr []interface{}) error {
|
||||
if value.CanAddr() && value.Addr().Type().Implements(typeScanner) {
|
||||
ptr[0] = value.Addr().Interface()
|
||||
return nil
|
||||
}
|
||||
switch value.Kind() {
|
||||
case reflect.Struct:
|
||||
s.findValueByName(value, name, ptr, true)
|
||||
return nil
|
||||
case reflect.Ptr:
|
||||
if value.IsNil() {
|
||||
value.Set(reflect.New(value.Type().Elem()))
|
||||
}
|
||||
return s.findPtr(value.Elem(), name, ptr)
|
||||
default:
|
||||
ptr[0] = value.Addr().Interface()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *tagStore) findValueByName(value reflect.Value, name []string, ret []interface{}, retPtr bool) {
|
||||
if value.Type().Implements(typeValuer) {
|
||||
return
|
||||
}
|
||||
switch value.Kind() {
|
||||
case reflect.Ptr:
|
||||
if value.IsNil() {
|
||||
return
|
||||
}
|
||||
s.findValueByName(value.Elem(), name, ret, retPtr)
|
||||
case reflect.Struct:
|
||||
l := s.get(value.Type())
|
||||
for i := 0; i < value.NumField(); i++ {
|
||||
tag := l[i]
|
||||
if tag == "" {
|
||||
continue
|
||||
}
|
||||
fieldValue := value.Field(i)
|
||||
for i, want := range name {
|
||||
if want != tag {
|
||||
continue
|
||||
}
|
||||
if ret[i] == nil {
|
||||
if retPtr {
|
||||
ret[i] = fieldValue.Addr().Interface()
|
||||
} else {
|
||||
ret[i] = fieldValue
|
||||
}
|
||||
}
|
||||
}
|
||||
s.findValueByName(fieldValue, name, ret, retPtr)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user