258 lines
6.9 KiB
Go
258 lines
6.9 KiB
Go
package stardb
|
|
|
|
import (
|
|
"errors"
|
|
"reflect"
|
|
"time"
|
|
)
|
|
|
|
// setStructFieldsFromRow sets struct fields from a row result using reflection
|
|
func (r *StarRows) setStructFieldsFromRow(target interface{}, tagKey string, rowIndex int) error {
|
|
targetType := reflect.TypeOf(target)
|
|
targetValue := reflect.ValueOf(target)
|
|
|
|
if targetType.Kind() == reflect.Ptr {
|
|
targetValue = targetValue.Elem()
|
|
}
|
|
|
|
if targetType.Kind() != reflect.Ptr && !targetValue.CanSet() {
|
|
return errors.New("target is not writable")
|
|
}
|
|
|
|
if targetType.Kind() == reflect.Ptr {
|
|
targetType = targetType.Elem()
|
|
}
|
|
|
|
if targetValue.Kind() != reflect.Struct {
|
|
return errors.New("target is not a struct")
|
|
}
|
|
|
|
for i := 0; i < targetType.NumField(); i++ {
|
|
field := targetType.Field(i)
|
|
fieldValue := targetValue.Field(i)
|
|
tagValue := field.Tag.Get(tagKey)
|
|
|
|
// Handle nested structs
|
|
if fieldValue.Kind() == reflect.Ptr && reflect.TypeOf(fieldValue.Interface()).Elem().Kind() == reflect.Struct {
|
|
if tagValue == "" {
|
|
continue
|
|
}
|
|
if tagValue == "---" {
|
|
nestedPtr := reflect.New(reflect.TypeOf(fieldValue.Interface()).Elem()).Interface()
|
|
r.setStructFieldsFromRow(nestedPtr, tagKey, rowIndex)
|
|
targetValue.Field(i).Set(reflect.ValueOf(nestedPtr))
|
|
continue
|
|
}
|
|
}
|
|
|
|
if fieldValue.Kind() == reflect.Struct {
|
|
if tagValue == "" {
|
|
continue
|
|
}
|
|
if tagValue == "---" {
|
|
nestedPtr := reflect.New(reflect.TypeOf(targetValue.Field(i).Interface())).Interface()
|
|
r.setStructFieldsFromRow(nestedPtr, tagKey, rowIndex)
|
|
targetValue.Field(i).Set(reflect.ValueOf(nestedPtr).Elem())
|
|
continue
|
|
}
|
|
}
|
|
|
|
if tagValue == "" {
|
|
continue
|
|
}
|
|
|
|
// Check if column exists
|
|
if _, ok := r.Row(rowIndex).columnIndex[tagValue]; !ok {
|
|
continue
|
|
}
|
|
|
|
// Set field value based on type
|
|
r.setFieldValue(fieldValue, tagValue, rowIndex)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// setFieldValue sets a single field value
|
|
func (r *StarRows) setFieldValue(fieldValue reflect.Value, columnName string, rowIndex int) {
|
|
row := r.Row(rowIndex)
|
|
|
|
switch fieldValue.Kind() {
|
|
case reflect.String:
|
|
fieldValue.SetString(row.MustString(columnName))
|
|
case reflect.Int:
|
|
fieldValue.SetInt(int64(row.MustInt(columnName)))
|
|
case reflect.Int8:
|
|
fieldValue.SetInt(int64(int8(row.MustInt64(columnName))))
|
|
case reflect.Int16:
|
|
fieldValue.SetInt(int64(int16(row.MustInt64(columnName))))
|
|
case reflect.Int32:
|
|
fieldValue.SetInt(int64(row.MustInt32(columnName)))
|
|
case reflect.Int64:
|
|
fieldValue.SetInt(row.MustInt64(columnName))
|
|
case reflect.Uint:
|
|
fieldValue.SetUint(uint64(row.MustUint64(columnName)))
|
|
case reflect.Uint8:
|
|
fieldValue.SetUint(uint64(uint8(row.MustUint64(columnName))))
|
|
case reflect.Uint16:
|
|
fieldValue.SetUint(uint64(uint16(row.MustUint64(columnName))))
|
|
case reflect.Uint32:
|
|
fieldValue.SetUint(uint64(uint32(row.MustUint64(columnName))))
|
|
case reflect.Uint64:
|
|
fieldValue.SetUint(row.MustUint64(columnName))
|
|
case reflect.Bool:
|
|
fieldValue.SetBool(row.MustBool(columnName))
|
|
case reflect.Float32:
|
|
fieldValue.SetFloat(float64(row.MustFloat32(columnName)))
|
|
case reflect.Float64:
|
|
fieldValue.SetFloat(row.MustFloat64(columnName))
|
|
case reflect.Interface, reflect.Struct, reflect.Ptr:
|
|
// Handle special types like time.Time
|
|
colIndex := r.columnIndex[columnName]
|
|
val := row.Result()[colIndex]
|
|
if t, ok := val.(time.Time); ok {
|
|
fieldValue.Set(reflect.ValueOf(t))
|
|
}
|
|
}
|
|
}
|
|
|
|
// getStructFieldValues extracts all field values from a struct
|
|
func getStructFieldValues(target interface{}, tagKey string) (map[string]interface{}, error) {
|
|
result := make(map[string]interface{})
|
|
targetType := reflect.TypeOf(target)
|
|
targetValue := reflect.ValueOf(target)
|
|
|
|
if targetType.Kind() == reflect.Ptr {
|
|
if targetValue.IsNil() {
|
|
return nil, errors.New("pointer target is nil")
|
|
}
|
|
targetType = targetType.Elem()
|
|
targetValue = targetValue.Elem()
|
|
}
|
|
|
|
if targetValue.Kind() != reflect.Struct {
|
|
return nil, errors.New("target is not a struct")
|
|
}
|
|
|
|
for i := 0; i < targetType.NumField(); i++ {
|
|
field := targetType.Field(i)
|
|
fieldValue := targetValue.Field(i)
|
|
tagValue := field.Tag.Get(tagKey)
|
|
|
|
// Handle nested pointer structs
|
|
if fieldValue.Kind() == reflect.Ptr && reflect.TypeOf(fieldValue.Interface()).Elem().Kind() == reflect.Struct {
|
|
if fieldValue.IsNil() {
|
|
continue
|
|
}
|
|
if tagValue == "---" {
|
|
nestedValues, err := getStructFieldValues(reflect.ValueOf(fieldValue.Elem().Interface()).Interface(), tagKey)
|
|
if err != nil {
|
|
return result, err
|
|
}
|
|
for k, v := range nestedValues {
|
|
result[k] = v
|
|
}
|
|
continue
|
|
}
|
|
}
|
|
|
|
// Handle nested structs
|
|
if targetValue.Field(i).Kind() == reflect.Struct {
|
|
if tagValue == "---" {
|
|
nestedValues, err := getStructFieldValues(targetValue.Field(i).Interface(), tagKey)
|
|
if err != nil {
|
|
return result, err
|
|
}
|
|
for k, v := range nestedValues {
|
|
result[k] = v
|
|
}
|
|
continue
|
|
}
|
|
}
|
|
|
|
if tagValue == "" {
|
|
continue
|
|
}
|
|
|
|
if !fieldValue.CanInterface() {
|
|
continue
|
|
}
|
|
|
|
result[tagValue] = fieldValue.Interface()
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// getStructFieldNames extracts all field names (tag values) from a struct
|
|
func getStructFieldNames(target interface{}, tagKey string) ([]string, error) {
|
|
var result []string
|
|
|
|
if !isStruct(target) {
|
|
return []string{}, errors.New("target is not a struct")
|
|
}
|
|
|
|
targetType := reflect.TypeOf(target)
|
|
targetValue := reflect.ValueOf(target)
|
|
|
|
if targetType.Kind() == reflect.Ptr {
|
|
if targetValue.IsNil() {
|
|
return []string{}, errors.New("pointer target is nil")
|
|
}
|
|
targetType = targetType.Elem()
|
|
targetValue = targetValue.Elem()
|
|
}
|
|
|
|
for i := 0; i < targetType.NumField(); i++ {
|
|
fieldValue := targetValue.Field(i)
|
|
field := targetType.Field(i)
|
|
tagValue := field.Tag.Get(tagKey)
|
|
|
|
// Handle nested pointer structs
|
|
if fieldValue.Kind() == reflect.Ptr && reflect.TypeOf(fieldValue.Interface()).Elem().Kind() == reflect.Struct {
|
|
if fieldValue.IsNil() {
|
|
continue
|
|
}
|
|
if tagValue == "---" {
|
|
nestedNames, err := getStructFieldNames(reflect.ValueOf(fieldValue.Elem().Interface()).Interface(), tagKey)
|
|
if err != nil {
|
|
return result, err
|
|
}
|
|
result = append(result, nestedNames...)
|
|
continue
|
|
}
|
|
}
|
|
|
|
// Handle nested structs
|
|
if targetValue.Field(i).Kind() == reflect.Struct && tagValue == "---" {
|
|
nestedNames, err := getStructFieldNames(targetValue.Field(i).Interface(), tagKey)
|
|
if err != nil {
|
|
return result, err
|
|
}
|
|
result = append(result, nestedNames...)
|
|
continue
|
|
}
|
|
|
|
if tagValue != "" {
|
|
result = append(result, tagValue)
|
|
}
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// isWritable checks if a value is writable
|
|
func isWritable(target interface{}) bool {
|
|
targetType := reflect.TypeOf(target)
|
|
targetValue := reflect.ValueOf(target)
|
|
return targetType.Kind() == reflect.Ptr || targetValue.CanSet()
|
|
}
|
|
|
|
// isStruct checks if a value is a struct
|
|
func isStruct(target interface{}) bool {
|
|
targetValue := reflect.ValueOf(target)
|
|
if targetValue.Kind() == reflect.Ptr {
|
|
targetValue = targetValue.Elem()
|
|
}
|
|
return targetValue.Kind() == reflect.Struct
|
|
}
|