github.com/phpstudyer/protoreflect@v1.7.2/dynamic/msgregistry/message_registry.go (about) 1 package msgregistry 2 3 import ( 4 "fmt" 5 "reflect" 6 "strings" 7 "sync" 8 9 "github.com/golang/protobuf/jsonpb" 10 "github.com/golang/protobuf/proto" 11 "github.com/golang/protobuf/protoc-gen-go/descriptor" 12 "github.com/golang/protobuf/ptypes" 13 "github.com/golang/protobuf/ptypes/any" 14 "github.com/golang/protobuf/ptypes/wrappers" 15 "google.golang.org/genproto/protobuf/api" 16 "google.golang.org/genproto/protobuf/ptype" 17 "google.golang.org/genproto/protobuf/source_context" 18 19 "github.com/phpstudyer/protoreflect/desc" 20 "github.com/phpstudyer/protoreflect/dynamic" 21 ) 22 23 const googleApisDomain = "type.googleapis.com" 24 25 // MessageRegistry is a registry that maps URLs to message types. It allows for marshalling 26 // and unmarshalling Any types to and from dynamic messages. 27 type MessageRegistry struct { 28 resolver typeResolver 29 mf *dynamic.MessageFactory 30 er *dynamic.ExtensionRegistry 31 mu sync.RWMutex 32 types map[string]desc.Descriptor 33 baseUrls map[string]string 34 defaultBaseUrl string 35 } 36 37 // NewMessageRegistryWithDefaults is a registry that includes all "default" message types, 38 // which are those that are statically linked into the current program (e.g. registered by 39 // protoc-generated code via proto.RegisterType). Note that it cannot resolve "default" enum 40 // types since those don't actually get registered by protoc-generated code the same way. 41 // Any types explicitly added to the registry will override any default message types with 42 // the same URL. 43 func NewMessageRegistryWithDefaults() *MessageRegistry { 44 mf := dynamic.NewMessageFactoryWithDefaults() 45 return &MessageRegistry{ 46 mf: mf, 47 er: mf.GetExtensionRegistry(), 48 } 49 } 50 51 // WithFetcher sets the TypeFetcher that this registry uses to resolve unknown URLs. If no fetcher 52 // is configured for the registry then unknown URLs cannot be resolved. Known URLs are those for 53 // explicitly registered types and, if the registry includes "default" types, those for statically 54 // linked message types. This method is not thread-safe and is intended to be used for one-time 55 // initialization of the registry, before it is published for use by other threads. 56 func (r *MessageRegistry) WithFetcher(fetcher TypeFetcher) *MessageRegistry { 57 r.resolver = typeResolver{fetcher: fetcher, mr: r} 58 return r 59 } 60 61 // WithMessageFactory sets the MessageFactory used to instantiate any messages. 62 // This method is not thread-safe and is intended to be used for one-time 63 // initialization of the registry, before it is published for use by other threads. 64 func (r *MessageRegistry) WithMessageFactory(mf *dynamic.MessageFactory) *MessageRegistry { 65 r.mf = mf 66 if mf == nil { 67 r.er = nil 68 } else { 69 r.er = mf.GetExtensionRegistry() 70 } 71 return r 72 } 73 74 // WithDefaultBaseUrl sets the default base URL used when constructing type URLs for 75 // marshalling messages as Any types and converting descriptors to well-known type 76 // descriptions (ptypes). If unspecified, the default base URL will be "type.googleapis.com". 77 // This method is not thread-safe and is intended to be used for one-time initialization 78 // of the registry, before it is published for use by other threads. 79 func (r *MessageRegistry) WithDefaultBaseUrl(baseUrl string) *MessageRegistry { 80 baseUrl = stripTrailingSlash(baseUrl) 81 r.defaultBaseUrl = baseUrl 82 return r 83 } 84 85 func stripTrailingSlash(url string) string { 86 if url[len(url)-1] == '/' { 87 return url[:len(url)-1] 88 } 89 return url 90 } 91 92 // AddMessage adds the given URL and associated message descriptor to the registry. 93 func (r *MessageRegistry) AddMessage(url string, md *desc.MessageDescriptor) error { 94 url = ensureScheme(url) 95 baseUrl := strings.TrimSuffix(url, "/"+md.GetFullyQualifiedName()) 96 if url == baseUrl { 97 return fmt.Errorf("URL %s is invalid: it should end with path element %s", url, md.GetFullyQualifiedName()) 98 } 99 r.mu.Lock() 100 defer r.mu.Unlock() 101 if r.types == nil { 102 r.types = map[string]desc.Descriptor{} 103 } 104 r.types[url] = md 105 if r.baseUrls == nil { 106 r.baseUrls = map[string]string{} 107 } 108 r.baseUrls[md.GetFullyQualifiedName()] = baseUrl 109 return nil 110 } 111 112 // AddEnum adds the given URL and associated enum descriptor to the registry. 113 func (r *MessageRegistry) AddEnum(url string, ed *desc.EnumDescriptor) error { 114 url = ensureScheme(url) 115 baseUrl := strings.TrimSuffix(url, "/"+ed.GetFullyQualifiedName()) 116 if url == baseUrl { 117 return fmt.Errorf("URL %s is invalid: it should end with path element %s", url, ed.GetFullyQualifiedName()) 118 } 119 r.mu.Lock() 120 defer r.mu.Unlock() 121 if r.types == nil { 122 r.types = map[string]desc.Descriptor{} 123 } 124 r.types[url] = ed 125 if r.baseUrls == nil { 126 r.baseUrls = map[string]string{} 127 } 128 r.baseUrls[ed.GetFullyQualifiedName()] = baseUrl 129 return nil 130 } 131 132 // AddFile adds to the registry all message and enum types in the given file. The URL for each type 133 // is derived using the given base URL as "baseURL/fully.qualified.type.name". 134 func (r *MessageRegistry) AddFile(baseUrl string, fd *desc.FileDescriptor) { 135 baseUrl = stripTrailingSlash(ensureScheme(baseUrl)) 136 r.mu.Lock() 137 defer r.mu.Unlock() 138 if r.types == nil { 139 r.types = map[string]desc.Descriptor{} 140 } 141 if r.baseUrls == nil { 142 r.baseUrls = map[string]string{} 143 } 144 r.addEnumTypesLocked(baseUrl, fd.GetEnumTypes()) 145 r.addMessageTypesLocked(baseUrl, fd.GetMessageTypes()) 146 } 147 148 func (r *MessageRegistry) addEnumTypesLocked(baseUrl string, enums []*desc.EnumDescriptor) { 149 for _, ed := range enums { 150 url := fmt.Sprintf("%s/%s", baseUrl, ed.GetFullyQualifiedName()) 151 r.types[url] = ed 152 r.baseUrls[ed.GetFullyQualifiedName()] = baseUrl 153 } 154 } 155 156 func (r *MessageRegistry) addMessageTypesLocked(baseUrl string, msgs []*desc.MessageDescriptor) { 157 for _, md := range msgs { 158 url := fmt.Sprintf("%s/%s", baseUrl, md.GetFullyQualifiedName()) 159 r.types[url] = md 160 r.baseUrls[md.GetFullyQualifiedName()] = baseUrl 161 r.addEnumTypesLocked(baseUrl, md.GetNestedEnumTypes()) 162 r.addMessageTypesLocked(baseUrl, md.GetNestedMessageTypes()) 163 } 164 } 165 166 // FindMessageTypeByUrl finds a message descriptor for the type at the given URL. It may 167 // return nil if the registry is empty and cannot resolve unknown URLs. If an error occurs 168 // while resolving the URL, it is returned. 169 func (r *MessageRegistry) FindMessageTypeByUrl(url string) (*desc.MessageDescriptor, error) { 170 md, err := r.getRegisteredMessageTypeByUrl(url) 171 if err != nil { 172 return nil, err 173 } else if md != nil { 174 return md, err 175 } 176 177 if r.resolver.fetcher == nil { 178 return nil, nil 179 } 180 return r.resolver.resolveUrlToMessageDescriptor(url) 181 } 182 183 func (r *MessageRegistry) getRegisteredMessageTypeByUrl(url string) (*desc.MessageDescriptor, error) { 184 if r != nil { 185 r.mu.RLock() 186 m := r.types[ensureScheme(url)] 187 r.mu.RUnlock() 188 if m != nil { 189 if md, ok := m.(*desc.MessageDescriptor); ok { 190 return md, nil 191 } else { 192 return nil, fmt.Errorf("type for URL %v is the wrong type: wanted message, got enum", url) 193 } 194 } 195 } 196 197 var ktr *dynamic.KnownTypeRegistry 198 if r != nil { 199 ktr = r.mf.GetKnownTypeRegistry() 200 } 201 msgType := ktr.GetKnownType(typeName(url)) 202 if msgType == nil { 203 return nil, nil 204 } 205 return desc.LoadMessageDescriptorForType(msgType) 206 } 207 208 // FindEnumTypeByUrl finds an enum descriptor for the type at the given URL. It may return nil 209 // if the registry is empty and cannot resolve unknown URLs. If an error occurs while resolving 210 // the URL, it is returned. 211 func (r *MessageRegistry) FindEnumTypeByUrl(url string) (*desc.EnumDescriptor, error) { 212 ed, err := r.getRegisteredEnumTypeByUrl(url) 213 if err != nil { 214 return nil, err 215 } else if ed != nil { 216 return ed, err 217 } 218 219 if r.resolver.fetcher == nil { 220 return nil, nil 221 } 222 if ed, err := r.resolver.resolveUrlToEnumDescriptor(url); err != nil { 223 return nil, err 224 } else { 225 return ed, nil 226 } 227 } 228 229 func (r *MessageRegistry) getRegisteredEnumTypeByUrl(url string) (*desc.EnumDescriptor, error) { 230 if r == nil { 231 return nil, nil 232 } 233 r.mu.RLock() 234 m := r.types[ensureScheme(url)] 235 r.mu.RUnlock() 236 if m != nil { 237 if ed, ok := m.(*desc.EnumDescriptor); ok { 238 return ed, nil 239 } else { 240 return nil, fmt.Errorf("type for URL %v is the wrong type: wanted enum, got message", url) 241 } 242 } 243 return nil, nil 244 } 245 246 // ResolveApiIntoServiceDescriptor constructs a service descriptor that describes the given API. 247 // If any of the service's request or response type URLs cannot be resolved by this registry, a 248 // nil descriptor is returned. 249 func (r *MessageRegistry) ResolveApiIntoServiceDescriptor(a *api.Api) (*desc.ServiceDescriptor, error) { 250 if r == nil { 251 return nil, nil 252 } 253 254 msgs := map[string]*desc.MessageDescriptor{} 255 unresolved := map[string]struct{}{} 256 for _, m := range a.Methods { 257 // request type 258 md, err := r.getRegisteredMessageTypeByUrl(m.RequestTypeUrl) 259 if err != nil { 260 return nil, err 261 } else if md == nil { 262 if r.resolver.fetcher == nil { 263 return nil, nil 264 } 265 unresolved[m.RequestTypeUrl] = struct{}{} 266 } else { 267 msgs[m.RequestTypeUrl] = md 268 } 269 // and response type 270 md, err = r.getRegisteredMessageTypeByUrl(m.ResponseTypeUrl) 271 if err != nil { 272 return nil, err 273 } else if md == nil { 274 if r.resolver.fetcher == nil { 275 return nil, nil 276 } 277 unresolved[m.ResponseTypeUrl] = struct{}{} 278 } else { 279 msgs[m.ResponseTypeUrl] = md 280 } 281 } 282 283 if len(unresolved) > 0 { 284 unresolvedSlice := make([]string, 0, len(unresolved)) 285 for k := range unresolved { 286 unresolvedSlice = append(unresolvedSlice, k) 287 } 288 mp, err := r.resolver.resolveUrlsToMessageDescriptors(unresolvedSlice...) 289 if err != nil { 290 return nil, err 291 } 292 for u, md := range mp { 293 msgs[u] = md 294 } 295 } 296 297 var fileName string 298 if a.SourceContext != nil && a.SourceContext.FileName != "" { 299 fileName = a.SourceContext.FileName 300 } else { 301 fileName = "--unknown--.proto" 302 } 303 304 // now we add all types we care about to a typeTrie and use that to generate file descriptors 305 files := map[string]*fileEntry{} 306 fe := &fileEntry{} 307 fe.proto3 = a.Syntax == ptype.Syntax_SYNTAX_PROTO3 308 files[fileName] = fe 309 fe.types.addType(a.Name, createServiceDescriptor(a, r)) 310 added := newNameTracker() 311 for _, md := range msgs { 312 addDescriptors(fileName, files, md, msgs, added) 313 } 314 315 // build resulting file descriptor(s) and return the final service descriptor 316 fileDescriptors, err := toFileDescriptors(files, (*typeTrie).rewriteDescriptor) 317 if err != nil { 318 return nil, err 319 } 320 return fileDescriptors[fileName].FindService(a.Name), nil 321 } 322 323 // UnmarshalAny will unmarshal the value embedded in the given Any value. This will use this 324 // registry to resolve the given value's type URL. Use this instead of ptypes.UnmarshalAny for 325 // cases where the type might not be statically linked into the current program. 326 func (r *MessageRegistry) UnmarshalAny(any *any.Any) (proto.Message, error) { 327 return r.unmarshalAny(any, r.FindMessageTypeByUrl) 328 } 329 330 func (r *MessageRegistry) unmarshalAny(any *any.Any, fetch func(string) (*desc.MessageDescriptor, error)) (proto.Message, error) { 331 name, err := ptypes.AnyMessageName(any) 332 if err != nil { 333 return nil, err 334 } 335 336 var msg proto.Message 337 338 var mf *dynamic.MessageFactory 339 var ktr *dynamic.KnownTypeRegistry 340 if r != nil { 341 mf = r.mf 342 ktr = r.mf.GetKnownTypeRegistry() 343 } 344 if msg = ktr.CreateIfKnown(name); msg == nil { 345 if md, err := fetch(any.TypeUrl); err != nil { 346 return nil, err 347 } else if md == nil { 348 return nil, fmt.Errorf("unknown message type: %s", any.TypeUrl) 349 } else { 350 msg = mf.NewDynamicMessage(md) 351 } 352 } 353 354 err = proto.Unmarshal(any.Value, msg) 355 if err != nil { 356 return nil, err 357 } else { 358 return msg, nil 359 } 360 } 361 362 // AddBaseUrlForElement adds a base URL for the given package or fully-qualified type name. 363 // This is used to construct type URLs for message types. If a given type has an associated 364 // base URL, it is used. Otherwise, the base URL for the type's package is used. If that is 365 // also absent, the registry's default base URL is used. 366 func (r *MessageRegistry) AddBaseUrlForElement(baseUrl, packageOrTypeName string) { 367 if baseUrl[len(baseUrl)-1] == '/' { 368 baseUrl = baseUrl[:len(baseUrl)-1] 369 } 370 r.mu.Lock() 371 defer r.mu.Unlock() 372 if r.baseUrls == nil { 373 r.baseUrls = map[string]string{} 374 } 375 r.baseUrls[packageOrTypeName] = baseUrl 376 } 377 378 // MarshalAny wraps the given message in an Any value. 379 func (r *MessageRegistry) MarshalAny(m proto.Message) (*any.Any, error) { 380 var md *desc.MessageDescriptor 381 if dm, ok := m.(*dynamic.Message); ok { 382 md = dm.GetMessageDescriptor() 383 } else { 384 var err error 385 md, err = desc.LoadMessageDescriptorForMessage(m) 386 if err != nil { 387 return nil, err 388 } 389 } 390 391 if b, err := proto.Marshal(m); err != nil { 392 return nil, err 393 } else { 394 return &any.Any{TypeUrl: r.ComputeUrl(md), Value: b}, nil 395 } 396 } 397 398 // MessageAsPType converts the given message descriptor into a ptype.Type. Registered 399 // base URLs are used to compute type URLs for any fields that have message or enum 400 // types. 401 func (r *MessageRegistry) MessageAsPType(md *desc.MessageDescriptor) *ptype.Type { 402 fs := md.GetFields() 403 fields := make([]*ptype.Field, len(fs)) 404 for i, f := range fs { 405 fields[i] = r.fieldAsPType(f) 406 } 407 oos := md.GetOneOfs() 408 oneOfs := make([]string, len(oos)) 409 for i, oo := range oos { 410 oneOfs[i] = oo.GetName() 411 } 412 return &ptype.Type{ 413 Name: md.GetFullyQualifiedName(), 414 Fields: fields, 415 Oneofs: oneOfs, 416 Options: r.options(md.GetOptions()), 417 Syntax: syntax(md.GetFile()), 418 SourceContext: &source_context.SourceContext{FileName: md.GetFile().GetName()}, 419 } 420 } 421 422 func (r *MessageRegistry) fieldAsPType(fd *desc.FieldDescriptor) *ptype.Field { 423 opts := r.options(fd.GetOptions()) 424 // remove the "packed" option as that is represented via separate field in ptype.Field 425 for i, o := range opts { 426 if o.Name == "packed" { 427 opts = append(opts[:i], opts[i+1:]...) 428 break 429 } 430 } 431 432 var oneOf int32 433 if fd.AsFieldDescriptorProto().OneofIndex != nil { 434 oneOf = fd.AsFieldDescriptorProto().GetOneofIndex() + 1 435 } 436 437 var card ptype.Field_Cardinality 438 switch fd.GetLabel() { 439 case descriptor.FieldDescriptorProto_LABEL_OPTIONAL: 440 card = ptype.Field_CARDINALITY_OPTIONAL 441 case descriptor.FieldDescriptorProto_LABEL_REPEATED: 442 card = ptype.Field_CARDINALITY_REPEATED 443 case descriptor.FieldDescriptorProto_LABEL_REQUIRED: 444 card = ptype.Field_CARDINALITY_REQUIRED 445 } 446 447 var url string 448 var kind ptype.Field_Kind 449 switch fd.GetType() { 450 case descriptor.FieldDescriptorProto_TYPE_ENUM: 451 kind = ptype.Field_TYPE_ENUM 452 url = r.ComputeUrl(fd.GetEnumType()) 453 case descriptor.FieldDescriptorProto_TYPE_GROUP: 454 kind = ptype.Field_TYPE_GROUP 455 url = r.ComputeUrl(fd.GetMessageType()) 456 case descriptor.FieldDescriptorProto_TYPE_MESSAGE: 457 kind = ptype.Field_TYPE_MESSAGE 458 url = r.ComputeUrl(fd.GetMessageType()) 459 case descriptor.FieldDescriptorProto_TYPE_BYTES: 460 kind = ptype.Field_TYPE_BYTES 461 case descriptor.FieldDescriptorProto_TYPE_STRING: 462 kind = ptype.Field_TYPE_STRING 463 case descriptor.FieldDescriptorProto_TYPE_BOOL: 464 kind = ptype.Field_TYPE_BOOL 465 case descriptor.FieldDescriptorProto_TYPE_DOUBLE: 466 kind = ptype.Field_TYPE_DOUBLE 467 case descriptor.FieldDescriptorProto_TYPE_FLOAT: 468 kind = ptype.Field_TYPE_FLOAT 469 case descriptor.FieldDescriptorProto_TYPE_FIXED32: 470 kind = ptype.Field_TYPE_FIXED32 471 case descriptor.FieldDescriptorProto_TYPE_FIXED64: 472 kind = ptype.Field_TYPE_FIXED64 473 case descriptor.FieldDescriptorProto_TYPE_INT32: 474 kind = ptype.Field_TYPE_INT32 475 case descriptor.FieldDescriptorProto_TYPE_INT64: 476 kind = ptype.Field_TYPE_INT64 477 case descriptor.FieldDescriptorProto_TYPE_SFIXED32: 478 kind = ptype.Field_TYPE_SFIXED32 479 case descriptor.FieldDescriptorProto_TYPE_SFIXED64: 480 kind = ptype.Field_TYPE_SFIXED64 481 case descriptor.FieldDescriptorProto_TYPE_SINT32: 482 kind = ptype.Field_TYPE_SINT32 483 case descriptor.FieldDescriptorProto_TYPE_SINT64: 484 kind = ptype.Field_TYPE_SINT64 485 case descriptor.FieldDescriptorProto_TYPE_UINT32: 486 kind = ptype.Field_TYPE_UINT32 487 case descriptor.FieldDescriptorProto_TYPE_UINT64: 488 kind = ptype.Field_TYPE_UINT64 489 } 490 491 return &ptype.Field{ 492 Name: fd.GetName(), 493 Number: fd.GetNumber(), 494 JsonName: fd.AsFieldDescriptorProto().GetJsonName(), 495 OneofIndex: oneOf, 496 DefaultValue: fd.AsFieldDescriptorProto().GetDefaultValue(), 497 Options: opts, 498 Packed: fd.GetFieldOptions().GetPacked(), 499 TypeUrl: url, 500 Cardinality: card, 501 Kind: kind, 502 } 503 } 504 505 // EnumAsPType converts the given enum descriptor into a ptype.Enum. 506 func (r *MessageRegistry) EnumAsPType(ed *desc.EnumDescriptor) *ptype.Enum { 507 vs := ed.GetValues() 508 vals := make([]*ptype.EnumValue, len(vs)) 509 for i, v := range vs { 510 vals[i] = r.enumValueAsPType(v) 511 } 512 return &ptype.Enum{ 513 Name: ed.GetFullyQualifiedName(), 514 Enumvalue: vals, 515 Options: r.options(ed.GetOptions()), 516 Syntax: syntax(ed.GetFile()), 517 SourceContext: &source_context.SourceContext{FileName: ed.GetFile().GetName()}, 518 } 519 } 520 521 func (r *MessageRegistry) enumValueAsPType(vd *desc.EnumValueDescriptor) *ptype.EnumValue { 522 return &ptype.EnumValue{ 523 Name: vd.GetName(), 524 Number: vd.GetNumber(), 525 Options: r.options(vd.GetOptions()), 526 } 527 } 528 529 // ServiceAsApi converts the given service descriptor into a ptype API description. 530 func (r *MessageRegistry) ServiceAsApi(sd *desc.ServiceDescriptor) *api.Api { 531 ms := sd.GetMethods() 532 methods := make([]*api.Method, len(ms)) 533 for i, m := range ms { 534 methods[i] = r.methodAsApi(m) 535 } 536 return &api.Api{ 537 Name: sd.GetFullyQualifiedName(), 538 Methods: methods, 539 Options: r.options(sd.GetOptions()), 540 Syntax: syntax(sd.GetFile()), 541 SourceContext: &source_context.SourceContext{FileName: sd.GetFile().GetName()}, 542 } 543 } 544 545 func (r *MessageRegistry) methodAsApi(md *desc.MethodDescriptor) *api.Method { 546 return &api.Method{ 547 Name: md.GetName(), 548 RequestStreaming: md.IsClientStreaming(), 549 ResponseStreaming: md.IsServerStreaming(), 550 RequestTypeUrl: r.ComputeUrl(md.GetInputType()), 551 ResponseTypeUrl: r.ComputeUrl(md.GetOutputType()), 552 Options: r.options(md.GetOptions()), 553 Syntax: syntax(md.GetFile()), 554 } 555 } 556 557 func (r *MessageRegistry) options(options proto.Message) []*ptype.Option { 558 rv := reflect.ValueOf(options) 559 if rv.Kind() == reflect.Ptr { 560 if rv.IsNil() { 561 return nil 562 } 563 rv = rv.Elem() 564 } 565 var opts []*ptype.Option 566 for _, p := range proto.GetProperties(rv.Type()).Prop { 567 if p.Tag == 0 { 568 continue 569 } 570 o := r.option(p.OrigName, rv.FieldByName(p.Name)) 571 if o != nil { 572 opts = append(opts, o...) 573 } 574 } 575 for _, ext := range proto.RegisteredExtensions(options) { 576 if proto.HasExtension(options, ext) { 577 v, err := proto.GetExtension(options, ext) 578 if err == nil && v != nil { 579 o := r.option(ext.Name, reflect.ValueOf(v)) 580 if o != nil { 581 opts = append(opts, o...) 582 } 583 } 584 } 585 } 586 return opts 587 } 588 589 var typeOfBytes = reflect.TypeOf([]byte(nil)) 590 591 func (r *MessageRegistry) option(name string, value reflect.Value) []*ptype.Option { 592 if value.Kind() == reflect.Slice && value.Type() != typeOfBytes { 593 // repeated field 594 ret := make([]*ptype.Option, value.Len()) 595 j := 0 596 for i := 0; i < value.Len(); i++ { 597 opt := r.singleOption(name, value.Index(i)) 598 if opt != nil { 599 ret[j] = opt 600 j++ 601 } 602 } 603 return ret[:j] 604 } else { 605 opt := r.singleOption(name, value) 606 if opt != nil { 607 return []*ptype.Option{opt} 608 } 609 return nil 610 } 611 } 612 613 func (r *MessageRegistry) singleOption(name string, value reflect.Value) *ptype.Option { 614 pm := wrap(value) 615 if pm == nil { 616 return nil 617 } 618 a, err := r.MarshalAny(pm) 619 if err != nil { 620 return nil 621 } 622 return &ptype.Option{ 623 Name: name, 624 Value: a, 625 } 626 } 627 628 func wrap(v reflect.Value) proto.Message { 629 if pm, ok := v.Interface().(proto.Message); ok { 630 return pm 631 } 632 if !v.IsValid() { 633 return nil 634 } 635 if v.Kind() == reflect.Ptr { 636 if v.IsNil() { 637 return nil 638 } 639 v = v.Elem() 640 } 641 switch v.Kind() { 642 case reflect.Bool: 643 return &wrappers.BoolValue{Value: v.Bool()} 644 case reflect.Slice: 645 if v.Type() != typeOfBytes { 646 panic(fmt.Sprintf("cannot convert/wrap %T as proto", v.Type())) 647 } 648 return &wrappers.BytesValue{Value: v.Bytes()} 649 case reflect.String: 650 return &wrappers.StringValue{Value: v.String()} 651 case reflect.Float32: 652 return &wrappers.FloatValue{Value: float32(v.Float())} 653 case reflect.Float64: 654 return &wrappers.DoubleValue{Value: v.Float()} 655 case reflect.Int32: 656 return &wrappers.Int32Value{Value: int32(v.Int())} 657 case reflect.Int64: 658 return &wrappers.Int64Value{Value: v.Int()} 659 case reflect.Uint32: 660 return &wrappers.UInt32Value{Value: uint32(v.Uint())} 661 case reflect.Uint64: 662 return &wrappers.UInt64Value{Value: v.Uint()} 663 default: 664 panic(fmt.Sprintf("cannot convert/wrap %T as proto", v.Type())) 665 } 666 } 667 668 func syntax(fd *desc.FileDescriptor) ptype.Syntax { 669 if fd.IsProto3() { 670 return ptype.Syntax_SYNTAX_PROTO3 671 } else { 672 return ptype.Syntax_SYNTAX_PROTO2 673 } 674 } 675 676 // ComputeUrl computes a type URL for element described by the given descriptor. 677 // The given descriptor must be an enum or message descriptor. This will use any 678 // registered URLs and base URLs to determine the appropriate URL for the given 679 // type. 680 // 681 // Deprecated: This method is deprecated due to its use of non-idiomatic naming. 682 // Use ComputeURL instead. 683 func (r *MessageRegistry) ComputeUrl(d desc.Descriptor) string { 684 return r.ComputeURL(d) 685 } 686 687 // ComputeURL computes a type URL string for the element described by the given 688 // descriptor. The given descriptor must be an enum or message descriptor. This 689 // will use any registered URLs and base URLs to determine the appropriate URL 690 // for the given type. 691 func (r *MessageRegistry) ComputeURL(d desc.Descriptor) string { 692 name, pkg := d.GetFullyQualifiedName(), d.GetFile().GetPackage() 693 r.mu.RLock() 694 baseUrl := r.baseUrls[name] 695 if baseUrl == "" { 696 // lookup domain for the package 697 baseUrl = r.baseUrls[pkg] 698 } 699 r.mu.RUnlock() 700 701 if baseUrl == "" { 702 baseUrl = r.defaultBaseUrl 703 if baseUrl == "" { 704 baseUrl = googleApisDomain 705 } 706 } 707 708 return fmt.Sprintf("%s/%s", baseUrl, name) 709 } 710 711 // Resolve resolves the given type URL into an instance of a message. This 712 // implements the jsonpb.AnyResolver interface, for use with marshaling and 713 // unmarshaling Any messages to/from JSON. 714 func (r *MessageRegistry) Resolve(typeUrl string) (proto.Message, error) { 715 md, err := r.FindMessageTypeByUrl(typeUrl) 716 if err != nil { 717 return nil, err 718 } 719 if md == nil { 720 return nil, fmt.Errorf("unknown message type: %s", typeUrl) 721 } 722 return r.mf.NewMessage(md), nil 723 } 724 725 var _ jsonpb.AnyResolver = (*MessageRegistry)(nil)