110 lines
2.8 KiB
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
|
|
}
|