nimo-shake/protocal/type_converter.go (212 lines of code) (raw):

package protocal import ( "fmt" conf "nimo-shake/configure" "reflect" "strconv" "time" "github.com/aws/aws-sdk-go/service/dynamodb" LOG "github.com/vinllen/log4go" bson2 "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" ) type TypeConverter struct { } // use dfs to convert to bson.M func (tc *TypeConverter) Run(input map[string]*dynamodb.AttributeValue) (interface{}, error) { v := reflect.ValueOf(input) if output := tc.dfs(v); output == nil { return RawData{}, fmt.Errorf("parse failed, return nil") } else if out, ok := output.(RawData); !ok { return RawData{}, fmt.Errorf("parse failed, return type isn't RawData") } else if _, ok := out.Data.(bson2.M); !ok { return RawData{}, fmt.Errorf("parse failed, return data isn't bson.M") } else { return out, nil } } func (tc *TypeConverter) dfs(v reflect.Value) interface{} { funcStartT := time.Now() defer LOG.Debug("dfs_func kind[%v] value[%v] duration[%v]", v.Kind().String(), v, time.Since(funcStartT)) switch v.Kind() { case reflect.Invalid: return nil case reflect.Slice, reflect.Array: if v.Len() == 0 { return nil } size := 0 ret := make([]interface{}, 0, v.Len()) for i := 0; i < v.Len(); i++ { out := tc.dfs(v.Index(i)) md := out.(RawData) size += md.Size ret = append(ret, md.Data) } return RawData{size, ret} case reflect.Struct: if v.NumField() == 0 { return nil } if v.Type().Name() == "bson.Decimal128" { return RawData{16, v} } size := 0 var ret interface{} cnt := 0 // at most one field in AttributeValue for i := 0; i < v.NumField(); i++ { name := v.Type().Field(i).Name if out := tc.dfs(v.Field(i)); out != nil { cnt++ if cnt > 2 { LOG.Crashf("illegal struct field number") } md := out.(RawData) size += md.Size md.Data = tc.convertToDetail(name, md.Data) ret = md.Data } } return RawData{size, ret} case reflect.Map: if len(v.MapKeys()) == 0 { return nil } size := 0 ret := make(bson2.M) for _, key := range v.MapKeys() { name := key.String() name = conf.ConvertIdFunc(name) if out := tc.dfs(v.MapIndex(key)); out != nil { md := out.(RawData) size += md.Size size += len(name) // out = tc.convertToDetail(name, md.Data, false) ret[name] = md.Data } } return RawData{size, ret} case reflect.Ptr: if v.IsNil() { return nil } else { return tc.dfs(v.Elem()) } case reflect.Interface: if v.IsNil() { return nil } else { return tc.dfs(v.Elem()) } case reflect.String: out := v.String() return RawData{len(out), out} case reflect.Int: fallthrough case reflect.Int64: return RawData{8, v.Int()} case reflect.Int8: return RawData{1, int8(v.Int())} case reflect.Int16: return RawData{2, int16(v.Int())} case reflect.Int32: return RawData{4, int32(v.Int())} case reflect.Uint: fallthrough case reflect.Uint64: return RawData{8, v.Uint()} case reflect.Uint8: return RawData{1, uint8(v.Uint())} case reflect.Uint16: return RawData{2, uint16(v.Uint())} case reflect.Uint32: return RawData{4, uint32(v.Uint())} case reflect.Bool: // fake size return RawData{1, v.Bool()} default: // not support LOG.Error("unknown type[%v]", v.Kind()) return nil } } func (tc *TypeConverter) convertToDetail(name string, input interface{}) interface{} { funcStartT := time.Now() defer LOG.Debug("convertToDetail_func name[%v] input[%v] duration[%v]", name, input, time.Since(funcStartT)) switch name { case "B": list := input.([]interface{}) output := make([]byte, 0, len(list)) for _, ele := range list { output = append(output, ele.(byte)) } return output case "BS": list := input.([]interface{}) output := make([][]byte, 0, len(list)) for _, ele := range list { inner := tc.convertToDetail("B", ele) output = append(output, inner.([]byte)) } return output case "NS": list := input.([]interface{}) var nType reflect.Type for _, ele := range list { inner := tc.convertToDetail("N", ele) nType = reflect.TypeOf(inner) break } if nType.Name() == "int" { output := make([]int, 0, len(list)) for _, ele := range list { inner := tc.convertToDetail("N", ele) output = append(output, inner.(int)) } return output } else { output := make([]primitive.Decimal128, 0, len(list)) for _, ele := range list { inner := tc.convertToDetail("N", ele) output = append(output, inner.(primitive.Decimal128)) } return output } case "SS": list := input.([]interface{}) output := make([]string, 0, len(list)) for _, ele := range list { inner := tc.convertToDetail("S", ele) output = append(output, inner.(string)) } return output case "L": list := input.([]interface{}) output := make([]interface{}, 0, len(list)) for _, ele := range list { output = append(output, ele) } return output case "BOOL": fallthrough case "NULL": return input.(bool) case "N": v := input.(string) val_int, err := strconv.Atoi(v) if err == nil { return val_int } val, err := primitive.ParseDecimal128(v) if err != nil { LOG.Error("convert N to decimal128 failed[%v]", err) val2, err := strconv.ParseFloat(v, 64) if err != nil { LOG.Crashf("convert N to decimal128 and float64 both failed[%v]", err) } val, _ = primitive.ParseDecimal128(fmt.Sprintf("%v", val2)) return val } return val case "S": return input.(string) } // "M" return input }