-
Notifications
You must be signed in to change notification settings - Fork 30
Expand file tree
/
Copy pathresolve.go
More file actions
153 lines (140 loc) · 5.85 KB
/
resolve.go
File metadata and controls
153 lines (140 loc) · 5.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// Copyright 2023-2026 Buf Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package protovalidate
import (
"buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/reflect/protoregistry"
)
//nolint:gochecknoglobals // static data, only want single instance
var resolver = newExtensionResolver()
// ResolveMessageRules returns the ResolveMessageRules option set for the MessageDescriptor.
func ResolveMessageRules(desc protoreflect.MessageDescriptor) (*validate.MessageRules, error) {
return resolve[*validate.MessageRules](desc.Options(), validate.E_Message)
}
// ResolveOneofRules returns the ResolveOneofRules option set for the OneofDescriptor.
func ResolveOneofRules(desc protoreflect.OneofDescriptor) (*validate.OneofRules, error) {
return resolve[*validate.OneofRules](desc.Options(), validate.E_Oneof)
}
// ResolveFieldRules returns the ResolveFieldRules option set for the FieldDescriptor.
func ResolveFieldRules(desc protoreflect.FieldDescriptor) (*validate.FieldRules, error) {
return resolve[*validate.FieldRules](desc.Options(), validate.E_Field)
}
// ResolvePredefinedRules returns the ResolvePredefinedRules option set for the
// FieldDescriptor. Note that this value is only meaningful if it is set on a
// field or extension of a field rule message. This method is provided for
// convenience.
func ResolvePredefinedRules(desc protoreflect.FieldDescriptor) (*validate.PredefinedRules, error) {
return resolve[*validate.PredefinedRules](desc.Options(), validate.E_Predefined)
}
// resolve resolves extensions without using [proto.GetExtension], in case the
// underlying type of the extension is not the concrete type expected by the
// library. In some cases, particularly when using a dynamic descriptor set, the
// underlying extension value's type will be a dynamicpb.Message. In some cases,
// the extension may not be resolved at all. This function handles reparsing the
// fields as needed to get it into the right concrete message. Resolve does not
// modify the input protobuf message, so it can be used concurrently.
func resolve[C proto.Message](
options proto.Message,
extensionType protoreflect.ExtensionType,
) (typedMessage C, err error) {
var nilMessage C
message, err := resolver.resolve(options, extensionType)
if err != nil {
return nilMessage, err
}
if message == nil {
return nilMessage, nil
} else if typedMessage, ok := message.(C); ok {
return typedMessage, nil
}
typedMessage, _ = typedMessage.ProtoReflect().New().Interface().(C)
b, err := proto.Marshal(message)
if err != nil {
return nilMessage, err
}
err = proto.Unmarshal(b, typedMessage)
if err != nil {
return nilMessage, err
}
return typedMessage, nil
}
// extensionResolver implements most of the logic of resolving protovalidate
// extensions.
type extensionResolver struct {
// types is a types that just contains the protovalidate extensions.
types *protoregistry.Types
}
// newExtensionResolver creates a new extension resolver. This is only called at
// init and will panic if it fails.
func newExtensionResolver() extensionResolver {
resolver := extensionResolver{
types: &protoregistry.Types{},
}
resolver.register(validate.E_Field)
resolver.register(validate.E_Message)
resolver.register(validate.E_Oneof)
resolver.register(validate.E_Predefined)
return resolver
}
// register registers an extension into the resolver's registry. This is only
// called at init and will panic if it fails.
func (resolver extensionResolver) register(extension protoreflect.ExtensionType) {
if err := resolver.types.RegisterExtension(extension); err != nil {
//nolint:forbidigo // this needs to be a fatal at init
panic(err)
}
}
// resolve handles the majority of extension resolution logic. This will return
// a proto.Message for the given extension if the message has the tag number of
// the provided extension. If there was no such tag number present in the known
// or unknown fields, this method will return nil. Note that the returned
// message may be dynamicpb.Message or another type, and thus may need to still
// be reparsed if needed.
func (resolver extensionResolver) resolve(
options proto.Message,
extensionType protoreflect.ExtensionType,
) (msg proto.Message, err error) {
msg = resolver.getExtension(options, extensionType)
if msg == nil {
if unknown := options.ProtoReflect().GetUnknown(); len(unknown) > 0 {
reparsedOptions := options.ProtoReflect().Type().New().Interface()
if err = (proto.UnmarshalOptions{
Resolver: resolver.types,
}).Unmarshal(unknown, reparsedOptions); err == nil {
msg = resolver.getExtension(reparsedOptions, extensionType)
}
}
}
if err != nil {
return nil, err
}
return msg, nil
}
// getExtension gets the extension extensionType on message, or if it is not
// found, nil. Unlike proto.GetExtension, this method will not panic if the
// runtime type of the extension is unexpected and returns nil if the extension
// is not present.
func (resolver extensionResolver) getExtension(
message proto.Message,
extensionType protoreflect.ExtensionType,
) proto.Message {
reflect := message.ProtoReflect()
if reflect.Has(extensionType.TypeDescriptor()) {
extension, _ := reflect.Get(extensionType.TypeDescriptor()).Interface().(protoreflect.Message)
return extension.Interface()
}
return nil
}