Skip to content

Commit e5ffd4a

Browse files
authored
Make constraint resolution more flexible to different concrete extension types (#57)
The go proto library struggles to resolve values for extensions if the concrete type associated with the generated source code on hand does not match that on the descriptor of the message in question (which happens in cases where the descriptor or message is produced at runtime). This manifested in the protovalidate resolver where the extension could not be found on options attached to a descriptor produced dynamically. Unfortunately, I struggled to find a way to get the extension and its value directly. (I'm not even sure how to get the field/ext descriptor from the descriptor; `protoreflect.Fields.ByNumber` doesn't work). The most straightforward solution from a few iterations was to leverage `proto.RangeExtensions`. There's room here for some mild optimization to short circuit in most vanilla situations but it makes it a bit less readable. Since this only happens once per descriptor, the optimization is overkill IMO.
1 parent ce1cbad commit e5ffd4a

File tree

1 file changed

+13
-28
lines changed

1 file changed

+13
-28
lines changed

internal/evaluator/resolver.go

Lines changed: 13 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import (
2020
"buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate"
2121
"google.golang.org/protobuf/proto"
2222
"google.golang.org/protobuf/reflect/protoreflect"
23-
"google.golang.org/protobuf/reflect/protoregistry"
2423
"google.golang.org/protobuf/runtime/protoimpl"
2524
)
2625

@@ -70,39 +69,25 @@ func resolveExt[D protoreflect.Descriptor, C proto.Message](
7069
desc D,
7170
extType protoreflect.ExtensionType,
7271
) (constraints C) {
73-
opts := desc.Options().ProtoReflect()
74-
fDesc := extType.TypeDescriptor()
75-
76-
if opts.Has(fDesc) {
77-
msg := opts.Get(fDesc).Message().Interface()
78-
if m, ok := msg.(C); ok {
79-
return m
72+
num := extType.TypeDescriptor().Number()
73+
var msg proto.Message
74+
proto.RangeExtensions(desc.Options(), func(typ protoreflect.ExtensionType, i interface{}) bool {
75+
if num != typ.TypeDescriptor().Number() {
76+
return true
8077
}
81-
}
82-
83-
unknown := opts.GetUnknown()
84-
if len(unknown) == 0 {
85-
return constraints
86-
}
78+
msg, _ = i.(proto.Message)
79+
return false
80+
})
8781

88-
opts = opts.Type().New()
89-
var resolver protoregistry.Types
90-
if err := resolver.RegisterExtension(extType); err != nil {
82+
if msg == nil {
9183
return constraints
92-
}
93-
_ = proto.UnmarshalOptions{Resolver: &resolver}.Unmarshal(unknown, opts.Interface())
94-
95-
if opts.Has(fDesc) {
96-
msg := opts.Get(fDesc).Message().Interface()
97-
if m, ok := msg.(C); ok {
98-
return m
99-
}
100-
}
101-
msg := opts.Get(fDesc).Message().Interface()
102-
if m, ok := msg.(C); ok {
84+
} else if m, ok := msg.(C); ok {
10385
return m
10486
}
10587

88+
constraints, _ = constraints.ProtoReflect().New().Interface().(C)
89+
b, _ := proto.Marshal(msg)
90+
_ = proto.Unmarshal(b, constraints)
10691
return constraints
10792
}
10893

0 commit comments

Comments
 (0)