mirror of
https://github.com/protocolbuffers/protobuf-go.git
synced 2025-01-30 21:32:43 +00:00
reflect/protodesc: add FileOptions
The FileOptions type provides the ability to specify specialized options for how a file descriptor is constructed. It follows the same optional arguments pattern as used in the proto package. The resolver is not an option since it almost always necessary when constructing a file descriptor. Change-Id: Ib98ac6289881ad8402dd615f6c895da5899cb8d9 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/218940 Reviewed-by: Damien Neil <dneil@google.com>
This commit is contained in:
parent
d2ece139c6
commit
25fc6fb4f2
@ -66,7 +66,7 @@ func TestNoGoPackage(t *testing.T) {
|
|||||||
Name: proto.String("testdata/go_package/no_go_package_import.proto"),
|
Name: proto.String("testdata/go_package/no_go_package_import.proto"),
|
||||||
Syntax: proto.String(protoreflect.Proto3.String()),
|
Syntax: proto.String(protoreflect.Proto3.String()),
|
||||||
Package: proto.String("goproto.testdata"),
|
Package: proto.String("goproto.testdata"),
|
||||||
Dependency: []string{"go_package/no_go_package.proto"},
|
Dependency: []string{"testdata/go_package/no_go_package.proto"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil)
|
}, nil)
|
||||||
|
@ -28,37 +28,40 @@ type Resolver interface {
|
|||||||
FindDescriptorByName(protoreflect.FullName) (protoreflect.Descriptor, error)
|
FindDescriptorByName(protoreflect.FullName) (protoreflect.Descriptor, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// option is an optional argument that may be provided to NewFile.
|
// FileOptions configures the construction of file descriptors.
|
||||||
type option struct {
|
type FileOptions struct {
|
||||||
pragma.DoNotCompare
|
pragma.NoUnkeyedLiterals
|
||||||
allowUnresolvable bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// allowUnresolvable configures NewFile to permissively allow unresolvable
|
// AllowUnresolvable configures New to permissively allow unresolvable
|
||||||
// file, enum, or message dependencies. Unresolved dependencies are replaced by
|
// file, enum, or message dependencies. Unresolved dependencies are replaced
|
||||||
// placeholder equivalents.
|
// by placeholder equivalents.
|
||||||
//
|
//
|
||||||
// The following dependencies may be left unresolved:
|
// The following dependencies may be left unresolved:
|
||||||
// • Resolving an imported file.
|
// • Resolving an imported file.
|
||||||
// • Resolving the type for a message field or extension field.
|
// • Resolving the type for a message field or extension field.
|
||||||
// If the kind of the field is unknown, then a placeholder is used for both
|
// If the kind of the field is unknown, then a placeholder is used for both
|
||||||
// protoreflect.FieldDescriptor.Enum and protoreflect.FieldDescriptor.Message.
|
// the Enum and Message accessors on the protoreflect.FieldDescriptor.
|
||||||
// • Resolving the enum value set as the default for an optional enum field.
|
// • Resolving an enum value set as the default for an optional enum field.
|
||||||
// If unresolvable, the protoreflect.FieldDescriptor.Default is set to the
|
// If unresolvable, the protoreflect.FieldDescriptor.Default is set to the
|
||||||
// first enum value in the associated enum (or zero if the also enum dependency
|
// first value in the associated enum (or zero if the also enum dependency
|
||||||
// is also unresolvable). The protoreflect.FieldDescriptor.DefaultEnumValue
|
// is also unresolvable). The protoreflect.FieldDescriptor.DefaultEnumValue
|
||||||
// is set as a placeholder.
|
// is populated with a placeholder.
|
||||||
// • Resolving the extended message type for an extension field.
|
// • Resolving the extended message type for an extension field.
|
||||||
// • Resolving the input or output message type for a service method.
|
// • Resolving the input or output message type for a service method.
|
||||||
//
|
//
|
||||||
// If the unresolved dependency uses a relative name, then the placeholder will
|
// If the unresolved dependency uses a relative name,
|
||||||
// contain an invalid FullName with a "*." prefix, indicating that the starting
|
// then the placeholder will contain an invalid FullName with a "*." prefix,
|
||||||
// prefix of the full name is unknown.
|
// indicating that the starting prefix of the full name is unknown.
|
||||||
func allowUnresolvable() option {
|
AllowUnresolvable bool
|
||||||
return option{allowUnresolvable: true}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFile creates a new protoreflect.FileDescriptor from the provided
|
// NewFile creates a new protoreflect.FileDescriptor from the provided
|
||||||
|
// file descriptor message. See FileOptions.New for more information.
|
||||||
|
func NewFile(fd *descriptorpb.FileDescriptorProto, r Resolver) (protoreflect.FileDescriptor, error) {
|
||||||
|
return FileOptions{}.New(fd, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new protoreflect.FileDescriptor from the provided
|
||||||
// file descriptor message. The file must represent a valid proto file according
|
// file descriptor message. The file must represent a valid proto file according
|
||||||
// to protobuf semantics. The returned descriptor is a deep copy of the input.
|
// to protobuf semantics. The returned descriptor is a deep copy of the input.
|
||||||
//
|
//
|
||||||
@ -66,16 +69,7 @@ func allowUnresolvable() option {
|
|||||||
// resolved using the provided registry. When looking up an import file path,
|
// resolved using the provided registry. When looking up an import file path,
|
||||||
// the path must be unique. The newly created file descriptor is not registered
|
// the path must be unique. The newly created file descriptor is not registered
|
||||||
// back into the provided file registry.
|
// back into the provided file registry.
|
||||||
func NewFile(fd *descriptorpb.FileDescriptorProto, r Resolver) (protoreflect.FileDescriptor, error) {
|
func (o FileOptions) New(fd *descriptorpb.FileDescriptorProto, r Resolver) (protoreflect.FileDescriptor, error) {
|
||||||
// TODO(blocks): remove setting allowUnresolvable once naughty users are migrated.
|
|
||||||
return newFile(fd, r, allowUnresolvable())
|
|
||||||
}
|
|
||||||
func newFile(fd *descriptorpb.FileDescriptorProto, r Resolver, opts ...option) (protoreflect.FileDescriptor, error) {
|
|
||||||
// Process the options.
|
|
||||||
var allowUnresolvable bool
|
|
||||||
for _, o := range opts {
|
|
||||||
allowUnresolvable = allowUnresolvable || o.allowUnresolvable
|
|
||||||
}
|
|
||||||
if r == nil {
|
if r == nil {
|
||||||
r = (*protoregistry.Files)(nil) // empty resolver
|
r = (*protoregistry.Files)(nil) // empty resolver
|
||||||
}
|
}
|
||||||
@ -120,7 +114,7 @@ func newFile(fd *descriptorpb.FileDescriptorProto, r Resolver, opts ...option) (
|
|||||||
for i, path := range fd.GetDependency() {
|
for i, path := range fd.GetDependency() {
|
||||||
imp := &f.L2.Imports[i]
|
imp := &f.L2.Imports[i]
|
||||||
f, err := r.FindFileByPath(path)
|
f, err := r.FindFileByPath(path)
|
||||||
if err == protoregistry.NotFound && (allowUnresolvable || imp.IsWeak) {
|
if err == protoregistry.NotFound && (o.AllowUnresolvable || imp.IsWeak) {
|
||||||
f = filedesc.PlaceholderFile(path)
|
f = filedesc.PlaceholderFile(path)
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return nil, errors.New("could not resolve import %q: %v", path, err)
|
return nil, errors.New("could not resolve import %q: %v", path, err)
|
||||||
@ -188,7 +182,7 @@ func newFile(fd *descriptorpb.FileDescriptorProto, r Resolver, opts ...option) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Step 2: Resolve every dependency reference not handled by step 1.
|
// Step 2: Resolve every dependency reference not handled by step 1.
|
||||||
r2 := &resolver{local: r1, remote: r, imports: imps, allowUnresolvable: allowUnresolvable}
|
r2 := &resolver{local: r1, remote: r, imports: imps, allowUnresolvable: o.AllowUnresolvable}
|
||||||
if err := r2.resolveMessageDependencies(f.L1.Messages.List, fd.GetMessageType()); err != nil {
|
if err := r2.resolveMessageDependencies(f.L1.Messages.List, fd.GetMessageType()); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -88,7 +88,7 @@ func TestNewFile(t *testing.T) {
|
|||||||
label string
|
label string
|
||||||
inDeps []*descriptorpb.FileDescriptorProto
|
inDeps []*descriptorpb.FileDescriptorProto
|
||||||
inDesc *descriptorpb.FileDescriptorProto
|
inDesc *descriptorpb.FileDescriptorProto
|
||||||
inOpts []option
|
inOpts FileOptions
|
||||||
wantDesc *descriptorpb.FileDescriptorProto
|
wantDesc *descriptorpb.FileDescriptorProto
|
||||||
wantErr string
|
wantErr string
|
||||||
}{{
|
}{{
|
||||||
@ -121,7 +121,7 @@ func TestNewFile(t *testing.T) {
|
|||||||
package: ""
|
package: ""
|
||||||
dependency: "dep.proto"
|
dependency: "dep.proto"
|
||||||
`),
|
`),
|
||||||
inOpts: []option{allowUnresolvable()},
|
inOpts: FileOptions{AllowUnresolvable: true},
|
||||||
}, {
|
}, {
|
||||||
label: "duplicate import",
|
label: "duplicate import",
|
||||||
inDesc: mustParseFile(`
|
inDesc: mustParseFile(`
|
||||||
@ -129,7 +129,7 @@ func TestNewFile(t *testing.T) {
|
|||||||
package: ""
|
package: ""
|
||||||
dependency: ["dep.proto", "dep.proto"]
|
dependency: ["dep.proto", "dep.proto"]
|
||||||
`),
|
`),
|
||||||
inOpts: []option{allowUnresolvable()},
|
inOpts: FileOptions{AllowUnresolvable: true},
|
||||||
wantErr: `already imported "dep.proto"`,
|
wantErr: `already imported "dep.proto"`,
|
||||||
}, {
|
}, {
|
||||||
label: "invalid weak import",
|
label: "invalid weak import",
|
||||||
@ -139,7 +139,7 @@ func TestNewFile(t *testing.T) {
|
|||||||
dependency: "dep.proto"
|
dependency: "dep.proto"
|
||||||
weak_dependency: [-23]
|
weak_dependency: [-23]
|
||||||
`),
|
`),
|
||||||
inOpts: []option{allowUnresolvable()},
|
inOpts: FileOptions{AllowUnresolvable: true},
|
||||||
wantErr: `invalid or duplicate weak import index: -23`,
|
wantErr: `invalid or duplicate weak import index: -23`,
|
||||||
}, {
|
}, {
|
||||||
label: "normal weak and public import",
|
label: "normal weak and public import",
|
||||||
@ -150,7 +150,7 @@ func TestNewFile(t *testing.T) {
|
|||||||
weak_dependency: [0]
|
weak_dependency: [0]
|
||||||
public_dependency: [0]
|
public_dependency: [0]
|
||||||
`),
|
`),
|
||||||
inOpts: []option{allowUnresolvable()},
|
inOpts: FileOptions{AllowUnresolvable: true},
|
||||||
}, {
|
}, {
|
||||||
label: "import public indirect dependency duplicate",
|
label: "import public indirect dependency duplicate",
|
||||||
inDeps: []*descriptorpb.FileDescriptorProto{
|
inDeps: []*descriptorpb.FileDescriptorProto{
|
||||||
@ -306,7 +306,7 @@ func TestNewFile(t *testing.T) {
|
|||||||
enum_type: [{name:"E" value:[{name:"V0" number:0}]}]
|
enum_type: [{name:"E" value:[{name:"V0" number:0}]}]
|
||||||
}]
|
}]
|
||||||
`),
|
`),
|
||||||
inOpts: []option{allowUnresolvable()},
|
inOpts: FileOptions{AllowUnresolvable: true},
|
||||||
}, {
|
}, {
|
||||||
label: "unresolved extendee",
|
label: "unresolved extendee",
|
||||||
inDesc: mustParseFile(`
|
inDesc: mustParseFile(`
|
||||||
@ -342,7 +342,7 @@ func TestNewFile(t *testing.T) {
|
|||||||
method: [{name:"M" input_type:"foo.bar.input" output_type:".absolute.foo.bar.output"}]
|
method: [{name:"M" input_type:"foo.bar.input" output_type:".absolute.foo.bar.output"}]
|
||||||
}]
|
}]
|
||||||
`),
|
`),
|
||||||
inOpts: []option{allowUnresolvable()},
|
inOpts: FileOptions{AllowUnresolvable: true},
|
||||||
}, {
|
}, {
|
||||||
label: "resolved but not imported",
|
label: "resolved but not imported",
|
||||||
inDeps: []*descriptorpb.FileDescriptorProto{mustParseFile(`
|
inDeps: []*descriptorpb.FileDescriptorProto{mustParseFile(`
|
||||||
@ -856,7 +856,7 @@ func TestNewFile(t *testing.T) {
|
|||||||
]
|
]
|
||||||
}]
|
}]
|
||||||
`),
|
`),
|
||||||
inOpts: []option{allowUnresolvable()},
|
inOpts: FileOptions{AllowUnresolvable: true},
|
||||||
// TODO: Test field and oneof handling in validateMessageDeclarations
|
// TODO: Test field and oneof handling in validateMessageDeclarations
|
||||||
// TODO: Test unmarshalDefault
|
// TODO: Test unmarshalDefault
|
||||||
// TODO: Test validateExtensionDeclarations
|
// TODO: Test validateExtensionDeclarations
|
||||||
@ -883,7 +883,7 @@ func TestNewFile(t *testing.T) {
|
|||||||
}]
|
}]
|
||||||
}]
|
}]
|
||||||
`),
|
`),
|
||||||
inOpts: []option{allowUnresolvable()},
|
inOpts: FileOptions{AllowUnresolvable: true},
|
||||||
}, {
|
}, {
|
||||||
label: "service with wrong reference type",
|
label: "service with wrong reference type",
|
||||||
inDeps: []*descriptorpb.FileDescriptorProto{
|
inDeps: []*descriptorpb.FileDescriptorProto{
|
||||||
@ -910,7 +910,7 @@ func TestNewFile(t *testing.T) {
|
|||||||
t.Run(tt.label, func(t *testing.T) {
|
t.Run(tt.label, func(t *testing.T) {
|
||||||
r := new(protoregistry.Files)
|
r := new(protoregistry.Files)
|
||||||
for i, dep := range tt.inDeps {
|
for i, dep := range tt.inDeps {
|
||||||
f, err := newFile(dep, r)
|
f, err := tt.inOpts.New(dep, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("dependency %d: unexpected NewFile() error: %v", i, err)
|
t.Fatalf("dependency %d: unexpected NewFile() error: %v", i, err)
|
||||||
}
|
}
|
||||||
@ -922,7 +922,7 @@ func TestNewFile(t *testing.T) {
|
|||||||
if tt.wantErr == "" && tt.wantDesc == nil {
|
if tt.wantErr == "" && tt.wantDesc == nil {
|
||||||
tt.wantDesc = cloneFile(tt.inDesc)
|
tt.wantDesc = cloneFile(tt.inDesc)
|
||||||
}
|
}
|
||||||
gotFile, err := newFile(tt.inDesc, r, tt.inOpts...)
|
gotFile, err := tt.inOpts.New(tt.inDesc, r)
|
||||||
if gotFile != nil {
|
if gotFile != nil {
|
||||||
gotDesc = ToFileDescriptorProto(gotFile)
|
gotDesc = ToFileDescriptorProto(gotFile)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user