package jsonpb import ( "net/url" "reflect" "strings" "kpt-tmr-group/pkg/xerr" "kpt-tmr-group/pkg/xreflect" "github.com/golang/protobuf/proto" ) var emptyField = reflect.StructField{} func UnmarshalQuery(values url.Values, pb proto.Message) error { target := reflect.ValueOf(pb).Elem() targetType := target.Type() if targetType.Kind() != reflect.Struct { return xerr.New("target should be struct") } sprops := proto.GetProperties(targetType) for i := 0; i < target.NumField(); i++ { ft := target.Type().Field(i) if strings.HasPrefix(ft.Name, "XXX_") { continue } getField := func(prop *proto.Properties) ([]string, bool) { // Be liberal in what names we accept; both orig_name and camelName are okay. camel, orig := prop.JSONName, prop.OrigName keys := []string{camel, orig} // handle condition xxx[]=1&xxx[]=2 if prop.Repeated { keys = append(keys, camel+"[]", orig+"[]") } for _, key := range keys { v, ok := values[key] if ok { return v, true } } return nil, false } valueForField, ok := getField(sprops.Prop[i]) if !ok { continue } err := setProtoByQueryValue(target.Field(i), valueForField, sprops.Prop[i]) if err != nil { return err } } return nil } func setProtoByQueryValue(target reflect.Value, queryValue []string, prop *proto.Properties) (err error) { if len(queryValue) == 0 { if prop.HasDefault { queryValue = []string{prop.Default} } else { return nil } } if target.Kind() == reflect.Slice { slice := reflect.MakeSlice(target.Type(), len(queryValue), len(queryValue)) for i, s := range queryValue { err := setProtoByQueryValue(slice.Index(i), []string{s}, prop) if err != nil { return err } } target.Set(slice) return nil } // Handle enums, which have an underlying type of int32, // and may appear as strings. // The case of an enum appearing as a number is handled // at the bottom of this function. if prop != nil && prop.Enum != "" { vmap := proto.EnumValueMap(prop.Enum) if len(queryValue) == 1 { // Don't need to do unquoting; valid enum names // are from a limited character set. s := queryValue[0] n, ok := vmap[s] if !ok { return xerr.Errorf("unknown value %q for enum %s", s, prop.Enum) } return setProtoEnum(n, target, prop) } } return xreflect.SetString(target, queryValue[0]) } func setProtoEnum(val int32, target reflect.Value, prop *proto.Properties) error { if target.Kind() == reflect.Ptr { // proto2 target.Set(reflect.New(target.Type().Elem())) target = target.Elem() } if target.Kind() != reflect.Int32 { return xerr.Errorf("invalid target %q for enum %s", target.Kind(), prop.Enum) } target.SetInt(int64(val)) return nil }