package external import ( "fmt" "reflect" "github.com/mitchellh/mapstructure" "google.golang.org/grpc/metadata" "github.com/hashicorp/consul/agent/structs" ) func StringToQueryBackendDecodeHookFunc(f reflect.Type, t reflect.Type, data any) (any, error) { if f.Kind() != reflect.String { return data, nil } if t != reflect.TypeOf(structs.QueryBackend(0)) { return data, nil } name, ok := data.(string) if !ok { return data, fmt.Errorf("could not parse query backend as string") } return structs.QueryBackendFromString(name), nil } // QueryMetaFromGRPCMeta returns a structs.QueryMeta struct parsed from the metadata.MD, // such as from a gRPC header or trailer. func QueryMetaFromGRPCMeta(md metadata.MD) (structs.QueryMeta, error) { var queryMeta structs.QueryMeta m := map[string]string{} for k, v := range md { m[k] = v[0] } decodeHooks := mapstructure.ComposeDecodeHookFunc( mapstructure.StringToTimeDurationHookFunc(), StringToQueryBackendDecodeHookFunc, ) config := &mapstructure.DecoderConfig{ Metadata: nil, Result: &queryMeta, WeaklyTypedInput: true, DecodeHook: decodeHooks, } decoder, err := mapstructure.NewDecoder(config) if err != nil { return queryMeta, err } err = decoder.Decode(m) if err != nil { return queryMeta, err } return queryMeta, nil } // GRPCMetadataFromQueryMeta returns a metadata struct with fields from the structs.QueryMeta attached. // The return value is suitable for attaching to a gRPC header/trailer. func GRPCMetadataFromQueryMeta(queryMeta structs.QueryMeta) (metadata.MD, error) { md := metadata.MD{} m := map[string]any{} err := mapstructure.Decode(queryMeta, &m) if err != nil { return nil, err } for k, v := range m { md.Set(k, fmt.Sprintf("%v", v)) } return md, nil }