query_decode.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. package jsonpb
  2. import (
  3. "net/url"
  4. "reflect"
  5. "strings"
  6. "kpt-tmr-group/pkg/xerr"
  7. "kpt-tmr-group/pkg/xreflect"
  8. "github.com/golang/protobuf/proto"
  9. )
  10. var emptyField = reflect.StructField{}
  11. func UnmarshalQuery(values url.Values, pb proto.Message) error {
  12. target := reflect.ValueOf(pb).Elem()
  13. targetType := target.Type()
  14. if targetType.Kind() != reflect.Struct {
  15. return xerr.New("target should be struct")
  16. }
  17. sprops := proto.GetProperties(targetType)
  18. for i := 0; i < target.NumField(); i++ {
  19. ft := target.Type().Field(i)
  20. if strings.HasPrefix(ft.Name, "XXX_") {
  21. continue
  22. }
  23. getField := func(prop *proto.Properties) ([]string, bool) {
  24. // Be liberal in what names we accept; both orig_name and camelName are okay.
  25. camel, orig := prop.JSONName, prop.OrigName
  26. keys := []string{camel, orig}
  27. // handle condition xxx[]=1&xxx[]=2
  28. if prop.Repeated {
  29. keys = append(keys, camel+"[]", orig+"[]")
  30. }
  31. for _, key := range keys {
  32. v, ok := values[key]
  33. if ok {
  34. return v, true
  35. }
  36. }
  37. return nil, false
  38. }
  39. valueForField, ok := getField(sprops.Prop[i])
  40. if !ok {
  41. continue
  42. }
  43. err := setProtoByQueryValue(target.Field(i), valueForField, sprops.Prop[i])
  44. if err != nil {
  45. return err
  46. }
  47. }
  48. return nil
  49. }
  50. func setProtoByQueryValue(target reflect.Value, queryValue []string, prop *proto.Properties) (err error) {
  51. if len(queryValue) == 0 {
  52. if prop.HasDefault {
  53. queryValue = []string{prop.Default}
  54. } else {
  55. return nil
  56. }
  57. }
  58. if target.Kind() == reflect.Slice {
  59. slice := reflect.MakeSlice(target.Type(), len(queryValue), len(queryValue))
  60. for i, s := range queryValue {
  61. err := setProtoByQueryValue(slice.Index(i), []string{s}, prop)
  62. if err != nil {
  63. return err
  64. }
  65. }
  66. target.Set(slice)
  67. return nil
  68. }
  69. // Handle enums, which have an underlying type of int32,
  70. // and may appear as strings.
  71. // The case of an enum appearing as a number is handled
  72. // at the bottom of this function.
  73. if prop != nil && prop.Enum != "" {
  74. vmap := proto.EnumValueMap(prop.Enum)
  75. if len(queryValue) == 1 {
  76. // Don't need to do unquoting; valid enum names
  77. // are from a limited character set.
  78. s := queryValue[0]
  79. n, ok := vmap[s]
  80. if !ok {
  81. return xerr.Errorf("unknown value %q for enum %s", s, prop.Enum)
  82. }
  83. return setProtoEnum(n, target, prop)
  84. }
  85. }
  86. return xreflect.SetString(target, queryValue[0])
  87. }
  88. func setProtoEnum(val int32, target reflect.Value, prop *proto.Properties) error {
  89. if target.Kind() == reflect.Ptr { // proto2
  90. target.Set(reflect.New(target.Type().Elem()))
  91. target = target.Elem()
  92. }
  93. if target.Kind() != reflect.Int32 {
  94. return xerr.Errorf("invalid target %q for enum %s", target.Kind(), prop.Enum)
  95. }
  96. target.SetInt(int64(val))
  97. return nil
  98. }