123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140 |
- package runtime
- import (
- "fmt"
- "net/url"
- "reflect"
- "strings"
- "github.com/golang/protobuf/proto"
- "github.com/grpc-ecosystem/grpc-gateway/utilities"
- "google.golang.org/grpc/grpclog"
- )
- // PopulateQueryParameters populates "values" into "msg".
- // A value is ignored if its key starts with one of the elements in "filter".
- func PopulateQueryParameters(msg proto.Message, values url.Values, filter *utilities.DoubleArray) error {
- for key, values := range values {
- fieldPath := strings.Split(key, ".")
- if filter.HasCommonPrefix(fieldPath) {
- continue
- }
- if err := populateFieldValueFromPath(msg, fieldPath, values); err != nil {
- return err
- }
- }
- return nil
- }
- // PopulateFieldFromPath sets a value in a nested Protobuf structure.
- // It instantiates missing protobuf fields as it goes.
- func PopulateFieldFromPath(msg proto.Message, fieldPathString string, value string) error {
- fieldPath := strings.Split(fieldPathString, ".")
- return populateFieldValueFromPath(msg, fieldPath, []string{value})
- }
- func populateFieldValueFromPath(msg proto.Message, fieldPath []string, values []string) error {
- m := reflect.ValueOf(msg)
- if m.Kind() != reflect.Ptr {
- return fmt.Errorf("unexpected type %T: %v", msg, msg)
- }
- m = m.Elem()
- for i, fieldName := range fieldPath {
- isLast := i == len(fieldPath)-1
- if !isLast && m.Kind() != reflect.Struct {
- return fmt.Errorf("non-aggregate type in the mid of path: %s", strings.Join(fieldPath, "."))
- }
- f := fieldByProtoName(m, fieldName)
- if !f.IsValid() {
- grpclog.Printf("field not found in %T: %s", msg, strings.Join(fieldPath, "."))
- return nil
- }
- switch f.Kind() {
- case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64, reflect.String, reflect.Uint32, reflect.Uint64:
- m = f
- case reflect.Slice:
- // TODO(yugui) Support []byte
- if !isLast {
- return fmt.Errorf("unexpected repeated field in %s", strings.Join(fieldPath, "."))
- }
- return populateRepeatedField(f, values)
- case reflect.Ptr:
- if f.IsNil() {
- m = reflect.New(f.Type().Elem())
- f.Set(m)
- }
- m = f.Elem()
- continue
- case reflect.Struct:
- m = f
- continue
- default:
- return fmt.Errorf("unexpected type %s in %T", f.Type(), msg)
- }
- }
- switch len(values) {
- case 0:
- return fmt.Errorf("no value of field: %s", strings.Join(fieldPath, "."))
- case 1:
- default:
- grpclog.Printf("too many field values: %s", strings.Join(fieldPath, "."))
- }
- return populateField(m, values[0])
- }
- // fieldByProtoName looks up a field whose corresponding protobuf field name is "name".
- // "m" must be a struct value. It returns zero reflect.Value if no such field found.
- func fieldByProtoName(m reflect.Value, name string) reflect.Value {
- props := proto.GetProperties(m.Type())
- for _, p := range props.Prop {
- if p.OrigName == name {
- return m.FieldByName(p.Name)
- }
- }
- return reflect.Value{}
- }
- func populateRepeatedField(f reflect.Value, values []string) error {
- elemType := f.Type().Elem()
- conv, ok := convFromType[elemType.Kind()]
- if !ok {
- return fmt.Errorf("unsupported field type %s", elemType)
- }
- f.Set(reflect.MakeSlice(f.Type(), len(values), len(values)))
- for i, v := range values {
- result := conv.Call([]reflect.Value{reflect.ValueOf(v)})
- if err := result[1].Interface(); err != nil {
- return err.(error)
- }
- f.Index(i).Set(result[0])
- }
- return nil
- }
- func populateField(f reflect.Value, value string) error {
- conv, ok := convFromType[f.Kind()]
- if !ok {
- return fmt.Errorf("unsupported field type %T", f)
- }
- result := conv.Call([]reflect.Value{reflect.ValueOf(value)})
- if err := result[1].Interface(); err != nil {
- return err.(error)
- }
- f.Set(result[0])
- return nil
- }
- var (
- convFromType = map[reflect.Kind]reflect.Value{
- reflect.String: reflect.ValueOf(String),
- reflect.Bool: reflect.ValueOf(Bool),
- reflect.Float64: reflect.ValueOf(Float64),
- reflect.Float32: reflect.ValueOf(Float32),
- reflect.Int64: reflect.ValueOf(Int64),
- reflect.Int32: reflect.ValueOf(Int32),
- reflect.Uint64: reflect.ValueOf(Uint64),
- reflect.Uint32: reflect.ValueOf(Uint32),
- // TODO(yugui) Support []byte
- }
- )
|