You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
119 lines
2.7 KiB
Go
119 lines
2.7 KiB
Go
2 years ago
|
package db
|
||
|
|
||
|
import (
|
||
|
"crypto/rand"
|
||
|
"encoding/hex"
|
||
|
"reflect"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
func randomHex(len int) string {
|
||
|
buff := make([]byte, len/2+1)
|
||
|
rand.Read(buff)
|
||
|
str := hex.EncodeToString(buff)
|
||
|
return str[:len]
|
||
|
}
|
||
|
|
||
|
func fieldInfo(fieldTyp reflect.StructField) (bool, string, reflect.Type, string) {
|
||
|
fieldName := fieldTyp.Name
|
||
|
columnName, ok := fieldTyp.Tag.Lookup("db")
|
||
|
if !ok {
|
||
|
columnName = strings.ToLower(fieldName)
|
||
|
}
|
||
|
if ok && columnName[len(columnName)-1] == '*' {
|
||
|
return true, fieldName, fieldTyp.Type, columnName[:len(columnName)-1]
|
||
|
}
|
||
|
|
||
|
return false, fieldName, fieldTyp.Type, columnName
|
||
|
}
|
||
|
|
||
|
func fieldPrimaryKey(fieldTyp reflect.StructField) bool {
|
||
|
isPK, _, _, _ := fieldInfo(fieldTyp)
|
||
|
return isPK
|
||
|
}
|
||
|
|
||
|
// structDatabaseColumnValues takes a pointer to a struct and returns a pair
|
||
|
// of slices, one with the column names and the other with pointers to all
|
||
|
// its exported fields.
|
||
|
//
|
||
|
// The column name can be changed using the "db" tag attribute on that field
|
||
|
func structDatabaseColumnValues(s any) ([]string, []any) {
|
||
|
v := reflect.ValueOf(s).Elem()
|
||
|
|
||
|
numFields := v.NumField()
|
||
|
|
||
|
names := []string{}
|
||
|
values := []any{}
|
||
|
|
||
|
for i := 0; i < numFields; i++ {
|
||
|
fieldTyp := v.Type().Field(i)
|
||
|
fieldVal := v.Field(i)
|
||
|
|
||
|
if fieldTyp.IsExported() {
|
||
|
key := strings.ToLower(fieldTyp.Name)
|
||
|
|
||
|
if name, ok := fieldTyp.Tag.Lookup("db"); ok {
|
||
|
key = name
|
||
|
|
||
|
// primary key has a "*" at the end, exclude from column name
|
||
|
if key[len(key)-1] == '*' {
|
||
|
key = key[:len(key)-1]
|
||
|
}
|
||
|
}
|
||
|
|
||
|
names = append(names, key)
|
||
|
values = append(values, fieldVal.Addr().Interface())
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return names, values
|
||
|
}
|
||
|
|
||
|
func structPrimaryKeyPtr(s any) (*string, bool) {
|
||
|
v := reflect.ValueOf(s).Elem()
|
||
|
|
||
|
for i := 0; i < v.NumField(); i++ {
|
||
|
fieldTyp := v.Type().Field(i)
|
||
|
fieldVal := v.Field(i)
|
||
|
|
||
|
if fieldTyp.IsExported() {
|
||
|
if fieldPrimaryKey(fieldTyp) {
|
||
|
// SAFETY: this is required to cast *Ref[T] to *string, internally
|
||
|
// they are the same type so this should be safe
|
||
|
ptr := fieldVal.Addr().UnsafePointer()
|
||
|
return (*string)(ptr), true
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return nil, false
|
||
|
}
|
||
|
|
||
|
// structDatabasePrimaryKeyColumn takes a pointer to a struct and returns the
|
||
|
// name of the field with the primary key
|
||
|
func structDatabasePrimaryKeyColumn(s any) (field, column string, ok bool) {
|
||
|
v := reflect.ValueOf(s).Elem()
|
||
|
|
||
|
for i := 0; i < v.NumField(); i++ {
|
||
|
fieldTyp := v.Type().Field(i)
|
||
|
if fieldTyp.IsExported() {
|
||
|
key := fieldTyp.Name
|
||
|
if value, ok := fieldTyp.Tag.Lookup("db"); ok {
|
||
|
if value[len(value)-1] == '*' {
|
||
|
return key, value[:len(value)-1], true
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return "", "", false
|
||
|
}
|
||
|
|
||
|
func repeatSlice[T any](value T, count int) []T {
|
||
|
s := make([]T, count)
|
||
|
for i := 0; i < count; i++ {
|
||
|
s[i] = value
|
||
|
}
|
||
|
return s
|
||
|
}
|