// Map receives a pointer to map or struct and maps it to columns and values. func Map(item interface{}, options *MapOptions) ([]string, []interface{}, error) { var fv fieldValue if options == nil { options = &defaultMapOptions } itemV := reflect.ValueOf(item) if !itemV.IsValid() { return nil, nil, nil } itemT := itemV.Type() if itemT.Kind() == reflect.Ptr { // Single dereference. Just in case the user passes a pointer to struct // instead of a struct. item = itemV.Elem().Interface() itemV = reflect.ValueOf(item) itemT = itemV.Type() } switch itemT.Kind() { case reflect.Struct: fieldMap := mapper.TypeMap(itemT).Names nfields := len(fieldMap) fv.values = make([]interface{}, 0, nfields) fv.fields = make([]string, 0, nfields) for _, fi := range fieldMap { // Field options _, tagOmitEmpty := fi.Options["omitempty"] _, tagStringArray := fi.Options["stringarray"] _, tagInt64Array := fi.Options["int64array"] _, tagJSONB := fi.Options["jsonb"] fld := reflectx.FieldByIndexesReadOnly(itemV, fi.Index) if fld.Kind() == reflect.Ptr && fld.IsNil() { if options.IncludeNil || !tagOmitEmpty { fv.fields = append(fv.fields, fi.Name) fv.values = append(fv.values, fld.Interface()) } continue } var value interface{} switch { case tagStringArray: v, ok := fld.Interface().([]string) if !ok { return nil, nil, fmt.Errorf(`Expecting field %q to be []string (using "stringarray" tag)`, fi.Name) } value = stringArray(v) case tagInt64Array: v, ok := fld.Interface().([]int64) if !ok { return nil, nil, fmt.Errorf(`Expecting field %q to be []int64 (using "int64array" tag)`, fi.Name) } value = int64Array(v) case tagJSONB: value = jsonbType{fld.Interface()} default: value = fld.Interface() } if !options.IncludeZeroed { if tagOmitEmpty { if t, ok := fld.Interface().(hasIsZero); ok { if t.IsZero() { continue } } else if fld.Kind() == reflect.Array || fld.Kind() == reflect.Slice { if value == nil { continue } } else if value == fi.Zero.Interface() { continue } } } fv.fields = append(fv.fields, fi.Name) v, err := marshal(value) if err != nil { return nil, nil, err } fv.values = append(fv.values, v) } case reflect.Map: nfields := itemV.Len() fv.values = make([]interface{}, nfields) fv.fields = make([]string, nfields) mkeys := itemV.MapKeys() for i, keyV := range mkeys { valv := itemV.MapIndex(keyV) fv.fields[i] = fmt.Sprintf("%v", keyV.Interface()) v, err := marshal(valv.Interface()) if err != nil { return nil, nil, err } fv.values[i] = v } default: return nil, nil, ErrExpectingPointerToEitherMapOrStruct } if len(fv.fields) == 0 { return nil, nil, errors.New("No values mapped.") } sort.Sort(&fv) return fv.fields, fv.values, nil }
func fetchResult(itemT reflect.Type, rows *sql.Rows, columns []string) (reflect.Value, error) { var item reflect.Value var err error objT := itemT switch objT.Kind() { case reflect.Map: item = reflect.MakeMap(objT) case reflect.Struct: item = reflect.New(objT) case reflect.Ptr: objT = itemT.Elem() if objT.Kind() != reflect.Struct { return item, ErrExpectingMapOrStruct } item = reflect.New(objT) default: return item, ErrExpectingMapOrStruct } switch objT.Kind() { case reflect.Struct: values := make([]interface{}, len(columns)) typeMap := mapper.TypeMap(itemT) fieldMap := typeMap.Names wrappedValues := map[*reflectx.FieldInfo]interface{}{} for i, k := range columns { fi, ok := fieldMap[k] if !ok { values[i] = new(interface{}) continue } // TODO: refactor into a nice pattern if _, ok := fi.Options["stringarray"]; ok { values[i] = &[]byte{} wrappedValues[fi] = values[i] } else if _, ok := fi.Options["int64array"]; ok { values[i] = &[]byte{} wrappedValues[fi] = values[i] } else if _, ok := fi.Options["jsonb"]; ok { values[i] = &[]byte{} wrappedValues[fi] = values[i] } else { f := reflectx.FieldByIndexes(item, fi.Index) values[i] = f.Addr().Interface() } if u, ok := values[i].(db.Unmarshaler); ok { values[i] = scanner{u} } } // Scanner - for reads // Valuer - for writes // OptionTypes // - before/after scan // - before/after valuer.. if err = rows.Scan(values...); err != nil { return item, err } // TODO: move this stuff out of here.. find a nice pattern for fi, v := range wrappedValues { var opt string if _, ok := fi.Options["stringarray"]; ok { opt = "stringarray" } else if _, ok := fi.Options["int64array"]; ok { opt = "int64array" } else if _, ok := fi.Options["jsonb"]; ok { opt = "jsonb" } b := v.(*[]byte) f := reflectx.FieldByIndexesReadOnly(item, fi.Index) switch opt { case "stringarray": v := stringArray{} err := v.Scan(*b) if err != nil { return item, err } f.Set(reflect.ValueOf(v)) case "int64array": v := int64Array{} err := v.Scan(*b) if err != nil { return item, err } f.Set(reflect.ValueOf(v)) case "jsonb": if len(*b) == 0 { continue } var vv reflect.Value t := reflect.PtrTo(f.Type()) switch t.Kind() { case reflect.Map: vv = reflect.MakeMap(t) case reflect.Slice: vv = reflect.MakeSlice(t, 0, 0) default: vv = reflect.New(t) } err := json.Unmarshal(*b, vv.Interface()) if err != nil { return item, err } vv = vv.Elem().Elem() if !vv.IsValid() || (vv.Kind() == reflect.Ptr && vv.IsNil()) { continue } f.Set(vv) } } case reflect.Map: columns, err := rows.Columns() if err != nil { return item, err } values := make([]interface{}, len(columns)) for i := range values { if itemT.Elem().Kind() == reflect.Interface { values[i] = new(interface{}) } else { values[i] = reflect.New(itemT.Elem()).Interface() } } if err = rows.Scan(values...); err != nil { return item, err } for i, column := range columns { item.SetMapIndex(reflect.ValueOf(column), reflect.Indirect(reflect.ValueOf(values[i]))) } } return item, nil }