Skip to content

Commit

Permalink
Make constraint resolution more flexible to different concrete extens…
Browse files Browse the repository at this point in the history
…ion 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.
  • Loading branch information
rodaine committed Oct 2, 2023
1 parent ce1cbad commit e5ffd4a
Showing 1 changed file with 13 additions and 28 deletions.
41 changes: 13 additions & 28 deletions internal/evaluator/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ 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"
"google.golang.org/protobuf/runtime/protoimpl"
)

Expand Down Expand Up @@ -70,39 +69,25 @@ func resolveExt[D protoreflect.Descriptor, C proto.Message](
desc D,
extType protoreflect.ExtensionType,
) (constraints C) {
opts := desc.Options().ProtoReflect()
fDesc := extType.TypeDescriptor()

if opts.Has(fDesc) {
msg := opts.Get(fDesc).Message().Interface()
if m, ok := msg.(C); ok {
return m
num := extType.TypeDescriptor().Number()
var msg proto.Message
proto.RangeExtensions(desc.Options(), func(typ protoreflect.ExtensionType, i interface{}) bool {
if num != typ.TypeDescriptor().Number() {
return true
}
}

unknown := opts.GetUnknown()
if len(unknown) == 0 {
return constraints
}
msg, _ = i.(proto.Message)
return false
})

opts = opts.Type().New()
var resolver protoregistry.Types
if err := resolver.RegisterExtension(extType); err != nil {
if msg == nil {
return constraints
}
_ = proto.UnmarshalOptions{Resolver: &resolver}.Unmarshal(unknown, opts.Interface())

if opts.Has(fDesc) {
msg := opts.Get(fDesc).Message().Interface()
if m, ok := msg.(C); ok {
return m
}
}
msg := opts.Get(fDesc).Message().Interface()
if m, ok := msg.(C); ok {
} else if m, ok := msg.(C); ok {
return m
}

constraints, _ = constraints.ProtoReflect().New().Interface().(C)
b, _ := proto.Marshal(msg)
_ = proto.Unmarshal(b, constraints)
return constraints
}

Expand Down

0 comments on commit e5ffd4a

Please sign in to comment.