func (r *MessageRegistry) MessageAsPType(md *desc.MessageDescriptor) *ptype.Type {
	fs := md.GetFields()
	fields := make([]*ptype.Field, len(fs))
	for i, f := range fs {
		fields[i] = r.fieldAsPType(f)
	}
	oos := md.GetOneOfs()
	oneOfs := make([]string, len(oos))
	for i, oo := range oos {
		oneOfs[i] = oo.GetName()
	}
	return &ptype.Type{
		Name:          md.GetFullyQualifiedName(),
		Fields:        fields,
		Oneofs:        oneOfs,
		Options:       r.options(md.GetOptions()),
		Syntax:        syntax(md.GetFile()),
		SourceContext: &source_context.SourceContext{FileName: md.GetFile().GetName()},
	}
}
MessageAsPType converts the given message descriptor into a ptype.Type. Registered
base URLs are used to compute type URLs for any fields that have message or enum
types.
package msgregistry
import (
	"fmt"
	"reflect"
	"strings"
	"sync"
	"github.com/golang/protobuf/jsonpb"
	"github.com/golang/protobuf/proto"
	"github.com/golang/protobuf/protoc-gen-go/descriptor"
	"github.com/golang/protobuf/ptypes"
	"github.com/golang/protobuf/ptypes/any"
	"github.com/golang/protobuf/ptypes/wrappers"
	"google.golang.org/genproto/protobuf/api"
	"google.golang.org/genproto/protobuf/ptype"
	"google.golang.org/genproto/protobuf/source_context"
	"github.com/jhump/protoreflect/desc"
	"github.com/jhump/protoreflect/dynamic"
)
const googleApisDomain = "type.googleapis.com"
type ErrUnexpectedType struct {
	URL          string
	ShouldBeEnum bool
}
func (e *ErrUnexpectedType) Error() string {
	msg := "wanted message, got enum"
	if e.ShouldBeEnum {
		msg = "wanted enum, got message"
	}
	return fmt.Sprintf("type for URL %q is the wrong type: %s", e.URL, msg)
}
type MessageRegistry struct {
	resolver       typeResolver
	mf             *dynamic.MessageFactory
	er             *dynamic.ExtensionRegistry
	mu             sync.RWMutex
	types          map[string]desc.Descriptor
	baseUrls       map[string]string
	defaultBaseUrl string
}
func NewMessageRegistryWithDefaults() *MessageRegistry {
	mf := dynamic.NewMessageFactoryWithDefaults()
	return &MessageRegistry{
		mf: mf,
		er: mf.GetExtensionRegistry(),
	}
}
func (r *MessageRegistry) WithFetcher(fetcher TypeFetcher) *MessageRegistry {
	r.resolver = typeResolver{fetcher: fetcher, mr: r}
	return r
}
func (r *MessageRegistry) WithMessageFactory(mf *dynamic.MessageFactory) *MessageRegistry {
	r.mf = mf
	if mf == nil {
		r.er = nil
	} else {
		r.er = mf.GetExtensionRegistry()
	}
	return r
}
func (r *MessageRegistry) WithDefaultBaseUrl(baseUrl string) *MessageRegistry {
	baseUrl = stripTrailingSlash(baseUrl)
	r.defaultBaseUrl = baseUrl
	return r
}
func stripTrailingSlash(url string) string {
	if url[len(url)-1] == '/' {
		return url[:len(url)-1]
	}
	return url
}
func (r *MessageRegistry) AddMessage(url string, md *desc.MessageDescriptor) error {
	url = ensureScheme(url)
	baseUrl := strings.TrimSuffix(url, "/"+md.GetFullyQualifiedName())
	if url == baseUrl {
		return fmt.Errorf("URL %s is invalid: it should end with path element %s", url, md.GetFullyQualifiedName())
	}
	r.mu.Lock()
	defer r.mu.Unlock()
	if r.types == nil {
		r.types = map[string]desc.Descriptor{}
	}
	r.types[url] = md
	if r.baseUrls == nil {
		r.baseUrls = map[string]string{}
	}
	r.baseUrls[md.GetFullyQualifiedName()] = baseUrl
	return nil
}
func (r *MessageRegistry) AddEnum(url string, ed *desc.EnumDescriptor) error {
	url = ensureScheme(url)
	baseUrl := strings.TrimSuffix(url, "/"+ed.GetFullyQualifiedName())
	if url == baseUrl {
		return fmt.Errorf("URL %s is invalid: it should end with path element %s", url, ed.GetFullyQualifiedName())
	}
	r.mu.Lock()
	defer r.mu.Unlock()
	if r.types == nil {
		r.types = map[string]desc.Descriptor{}
	}
	r.types[url] = ed
	if r.baseUrls == nil {
		r.baseUrls = map[string]string{}
	}
	r.baseUrls[ed.GetFullyQualifiedName()] = baseUrl
	return nil
}
func (r *MessageRegistry) AddFile(baseUrl string, fd *desc.FileDescriptor) {
	baseUrl = stripTrailingSlash(ensureScheme(baseUrl))
	r.mu.Lock()
	defer r.mu.Unlock()
	if r.types == nil {
		r.types = map[string]desc.Descriptor{}
	}
	if r.baseUrls == nil {
		r.baseUrls = map[string]string{}
	}
	r.addEnumTypesLocked(baseUrl, fd.GetEnumTypes())
	r.addMessageTypesLocked(baseUrl, fd.GetMessageTypes())
}
func (r *MessageRegistry) addEnumTypesLocked(baseUrl string, enums []*desc.EnumDescriptor) {
	for _, ed := range enums {
		url := fmt.Sprintf("%s/%s", baseUrl, ed.GetFullyQualifiedName())
		r.types[url] = ed
		r.baseUrls[ed.GetFullyQualifiedName()] = baseUrl
	}
}
func (r *MessageRegistry) addMessageTypesLocked(baseUrl string, msgs []*desc.MessageDescriptor) {
	for _, md := range msgs {
		url := fmt.Sprintf("%s/%s", baseUrl, md.GetFullyQualifiedName())
		r.types[url] = md
		r.baseUrls[md.GetFullyQualifiedName()] = baseUrl
		r.addEnumTypesLocked(baseUrl, md.GetNestedEnumTypes())
		r.addMessageTypesLocked(baseUrl, md.GetNestedMessageTypes())
	}
}
func (r *MessageRegistry) FindMessageTypeByUrl(url string) (*desc.MessageDescriptor, error) {
	md, err := r.getRegisteredMessageTypeByUrl(url)
	if err != nil {
		return nil, err
	} else if md != nil {
		return md, err
	}
	if r.resolver.fetcher == nil {
		return nil, nil
	}
	return r.resolver.resolveUrlToMessageDescriptor(url)
}
func (r *MessageRegistry) getRegisteredMessageTypeByUrl(url string) (*desc.MessageDescriptor, error) {
	if r != nil {
		r.mu.RLock()
		m := r.types[ensureScheme(url)]
		r.mu.RUnlock()
		if m != nil {
			if md, ok := m.(*desc.MessageDescriptor); ok {
				return md, nil
			}
			return nil, &ErrUnexpectedType{
				URL: url,
			}
		}
	}
	var ktr *dynamic.KnownTypeRegistry
	if r != nil {
		ktr = r.mf.GetKnownTypeRegistry()
	}
	msgType := ktr.GetKnownType(typeName(url))
	if msgType == nil {
		return nil, nil
	}
	return desc.LoadMessageDescriptorForType(msgType)
}
func (r *MessageRegistry) FindEnumTypeByUrl(url string) (*desc.EnumDescriptor, error) {
	ed, err := r.getRegisteredEnumTypeByUrl(url)
	if err != nil {
		return nil, err
	} else if ed != nil {
		return ed, err
	}
	if r.resolver.fetcher == nil {
		return nil, nil
	}
	if ed, err := r.resolver.resolveUrlToEnumDescriptor(url); err != nil {
		return nil, err
	} else {
		return ed, nil
	}
}
func (r *MessageRegistry) getRegisteredEnumTypeByUrl(url string) (*desc.EnumDescriptor, error) {
	if r == nil {
		return nil, nil
	}
	r.mu.RLock()
	m := r.types[ensureScheme(url)]
	r.mu.RUnlock()
	if m != nil {
		if ed, ok := m.(*desc.EnumDescriptor); ok {
			return ed, nil
		}
		return nil, &ErrUnexpectedType{
			URL: url,
		}
	}
	return nil, nil
}
func (r *MessageRegistry) ResolveApiIntoServiceDescriptor(a *api.Api) (*desc.ServiceDescriptor, error) {
	if r == nil {
		return nil, nil
	}
	msgs := map[string]*desc.MessageDescriptor{}
	unresolved := map[string]struct{}{}
	for _, m := range a.Methods {
		md, err := r.getRegisteredMessageTypeByUrl(m.RequestTypeUrl)
		if err != nil {
			return nil, err
		} else if md == nil {
			if r.resolver.fetcher == nil {
				return nil, nil
			}
			unresolved[m.RequestTypeUrl] = struct{}{}
		} else {
			msgs[m.RequestTypeUrl] = md
		}
		md, err = r.getRegisteredMessageTypeByUrl(m.ResponseTypeUrl)
		if err != nil {
			return nil, err
		} else if md == nil {
			if r.resolver.fetcher == nil {
				return nil, nil
			}
			unresolved[m.ResponseTypeUrl] = struct{}{}
		} else {
			msgs[m.ResponseTypeUrl] = md
		}
	}
	if len(unresolved) > 0 {
		unresolvedSlice := make([]string, 0, len(unresolved))
		for k := range unresolved {
			unresolvedSlice = append(unresolvedSlice, k)
		}
		mp, err := r.resolver.resolveUrlsToMessageDescriptors(unresolvedSlice...)
		if err != nil {
			return nil, err
		}
		for u, md := range mp {
			msgs[u] = md
		}
	}
	var fileName string
	if a.SourceContext != nil && a.SourceContext.FileName != "" {
		fileName = a.SourceContext.FileName
	} else {
		fileName = "--unknown--.proto"
	}
	files := map[string]*fileEntry{}
	fe := &fileEntry{}
	fe.proto3 = a.Syntax == ptype.Syntax_SYNTAX_PROTO3
	files[fileName] = fe
	fe.types.addType(a.Name, createServiceDescriptor(a, r))
	added := newNameTracker()
	for _, md := range msgs {
		addDescriptors(fileName, files, md, msgs, added)
	}
	fileDescriptors, err := toFileDescriptors(files, (*typeTrie).rewriteDescriptor)
	if err != nil {
		return nil, err
	}
	return fileDescriptors[fileName].FindService(a.Name), nil
}
func (r *MessageRegistry) UnmarshalAny(any *any.Any) (proto.Message, error) {
	return r.unmarshalAny(any, r.FindMessageTypeByUrl)
}
func (r *MessageRegistry) unmarshalAny(any *any.Any, fetch func(string) (*desc.MessageDescriptor, error)) (proto.Message, error) {
	name, err := ptypes.AnyMessageName(any)
	if err != nil {
		return nil, err
	}
	var msg proto.Message
	var mf *dynamic.MessageFactory
	var ktr *dynamic.KnownTypeRegistry
	if r != nil {
		mf = r.mf
		ktr = r.mf.GetKnownTypeRegistry()
	}
	if msg = ktr.CreateIfKnown(name); msg == nil {
		if md, err := fetch(any.TypeUrl); err != nil {
			return nil, err
		} else if md == nil {
			return nil, fmt.Errorf("unknown message type: %s", any.TypeUrl)
		} else {
			msg = mf.NewDynamicMessage(md)
		}
	}
	err = proto.Unmarshal(any.Value, msg)
	if err != nil {
		return nil, err
	} else {
		return msg, nil
	}
}
func (r *MessageRegistry) AddBaseUrlForElement(baseUrl, packageOrTypeName string) {
	if baseUrl[len(baseUrl)-1] == '/' {
		baseUrl = baseUrl[:len(baseUrl)-1]
	}
	r.mu.Lock()
	defer r.mu.Unlock()
	if r.baseUrls == nil {
		r.baseUrls = map[string]string{}
	}
	r.baseUrls[packageOrTypeName] = baseUrl
}
func (r *MessageRegistry) MarshalAny(m proto.Message) (*any.Any, error) {
	var md *desc.MessageDescriptor
	if dm, ok := m.(*dynamic.Message); ok {
		md = dm.GetMessageDescriptor()
	} else {
		var err error
		md, err = desc.LoadMessageDescriptorForMessage(m)
		if err != nil {
			return nil, err
		}
	}
	if b, err := proto.Marshal(m); err != nil {
		return nil, err
	} else {
		return &any.Any{TypeUrl: r.ComputeUrl(md), Value: b}, nil
	}
}