package httptt import ( "encoding/json" "fmt" "math" "net/url" "reflect" "strings" "kpt-tmr-group/pkg/xerr" "github.com/golang/protobuf/proto" ) type wkt interface { XXX_WellKnownType() string } var wktType = reflect.TypeOf((*wkt)(nil)).Elem() func MarshalQueryPB(pb proto.Message) *url.Values { fields := make(url.Values) target := reflect.ValueOf(pb).Elem() props := proto.GetProperties(target.Type()) for i := 0; i < target.NumField(); i++ { ft := target.Type().Field(i) if strings.HasPrefix(ft.Name, "XXX_") || ft.Anonymous { continue } // unexported field if strings.ToUpper(ft.Name[:1]) != ft.Name[:1] { continue } if err := setFields(&fields, target.Field(i), props.Prop[i]); err != nil { panic(err) } } return &fields } func setFields(fields *url.Values, target reflect.Value, prop *proto.Properties) error { if target.Kind() == reflect.Slice && target.Type().Elem().Kind() != reflect.Uint8 { for i := 0; i < target.Len(); i++ { if err := setFields(fields, target.Index(i), prop); err != nil { return xerr.WithStack(err) } } return nil } // Handle well-known types. // Most are handled up in marshalObject (because 99% are messages). if target.Type().Implements(wktType) { wkt := target.Interface().(wkt) switch wkt.XXX_WellKnownType() { case "NullValue": appendFields(fields, prop, "null") return nil } } if prop.Enum != "" { appendFields(fields, prop, target.Interface().(fmt.Stringer).String()) return nil } if target.Kind() == reflect.Float32 || target.Kind() == reflect.Float64 { f := target.Float() var sval string switch { case math.IsInf(f, 1): sval = `"Infinity"` case math.IsInf(f, -1): sval = `"-Infinity"` case math.IsNaN(f): sval = `"NaN"` } if sval != "" { appendFields(fields, prop, sval) return nil } } b, err := json.Marshal(target.Interface()) if err != nil { return xerr.WithStack(err) } appendFields(fields, prop, string(b)) return nil } func appendFields(v *url.Values, prop *proto.Properties, value string) { key := prop.JSONName if key == "" { key = prop.OrigName } v.Add(key, value) }