dbr/struct_mapping.go

110 lines
2.8 KiB
Go

package dbr
import (
"errors"
"fmt"
"reflect"
)
var destDummy interface{}
type fieldMapQueueElement struct {
Type reflect.Type
Idxs []int
}
// recordType is the type of a structure
func (sess *Session) calculateFieldMap(recordType reflect.Type, columns []string, requireAllColumns bool) ([][]int, error) {
// each value is either the slice to get to the field via FieldByIndex(index []int) in the record, or nil if we don't want to map it to the structure.
lenColumns := len(columns)
fieldMap := make([][]int, lenColumns)
for i, col := range columns {
fieldMap[i] = nil
queue := []fieldMapQueueElement{fieldMapQueueElement{Type: recordType, Idxs: nil}}
QueueLoop:
for len(queue) > 0 {
curEntry := queue[0]
queue = queue[1:]
curType := curEntry.Type
curIdxs := curEntry.Idxs
lenFields := curType.NumField()
for j := 0; j < lenFields; j++ {
fieldStruct := curType.Field(j)
// Skip unexported field
if len(fieldStruct.PkgPath) != 0 {
continue
}
name := fieldStruct.Tag.Get("db")
if name != "-" {
if name == "" {
name = NameMapping(fieldStruct.Name)
}
if name == col {
fieldMap[i] = append(curIdxs, j)
break QueueLoop
}
}
if fieldStruct.Type.Kind() == reflect.Struct {
var idxs2 []int
copy(idxs2, curIdxs)
idxs2 = append(idxs2, j)
queue = append(queue, fieldMapQueueElement{Type: fieldStruct.Type, Idxs: idxs2})
}
}
}
if requireAllColumns && fieldMap[i] == nil {
return nil, errors.New(fmt.Sprint("couldn't find match for column ", col))
}
}
return fieldMap, nil
}
func (sess *Session) prepareHolderFor(record reflect.Value, fieldMap [][]int, holder []interface{}) ([]interface{}, error) {
// Given a query and given a structure (field list), there's 2 sets of fields.
// Take the intersection. We can fill those in. great.
// For fields in the structure that aren't in the query, we'll let that slide if db:"-"
// For fields in the structure that aren't in the query but without db:"-", return error
// For fields in the query that aren't in the structure, we'll ignore them.
for i, fieldIndex := range fieldMap {
if fieldIndex == nil {
holder[i] = &destDummy
} else {
field := record.FieldByIndex(fieldIndex)
holder[i] = field.Addr().Interface()
}
}
return holder, nil
}
func (sess *Session) valuesFor(recordType reflect.Type, record reflect.Value, columns []string) ([]interface{}, error) {
fieldMap, err := sess.calculateFieldMap(recordType, columns, true)
if err != nil {
fmt.Println("err: calc field map")
return nil, err
}
values := make([]interface{}, len(columns))
for i, fieldIndex := range fieldMap {
if fieldIndex == nil {
panic("wtf bro")
} else {
field := record.FieldByIndex(fieldIndex)
values[i] = field.Interface()
}
}
return values, nil
}