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 }