Files
bobiqq c231be0094
build-and-push / detect (push) Successful in 7s
build-and-push / build (${{ fromJSON(needs.detect.outputs.services) }}) (push) Failing after 3m33s
services
2026-08-26 11:04:23 +03:00

142 lines
2.7 KiB
Go

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)
}
}
}