blob: 45ae144a2ca7607c9662b20be79264adf183dbb0 [file] [log] [blame]
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package protodetect provides function to identify the go_api_flag
// file and message options specified in the files.
package protodetect
import (
"fmt"
"google.golang.org/protobuf/compiler/protogen"
"google.golang.org/protobuf/proto"
pb "google.golang.org/open2opaque/internal/apiflagdata"
descpb "google.golang.org/protobuf/types/descriptorpb"
gofeaturespb "google.golang.org/protobuf/types/gofeaturespb"
pluginpb "google.golang.org/protobuf/types/pluginpb"
)
func fromFeatureToOld(apiLevel gofeaturespb.GoFeatures_APILevel) pb.GoAPI {
switch apiLevel {
case gofeaturespb.GoFeatures_API_OPEN:
return pb.GoAPI_OPEN_V1
case gofeaturespb.GoFeatures_API_HYBRID:
return pb.GoAPI_OPEN_TO_OPAQUE_HYBRID
case gofeaturespb.GoFeatures_API_OPAQUE:
return pb.GoAPI_OPAQUE_V0
default:
panic(fmt.Sprintf("unknown apilevel %v", apiLevel))
}
}
func apiLevelForDescriptor(fd *descpb.FileDescriptorProto) gofeaturespb.GoFeatures_APILevel {
return apiLevelForDescriptorOpts(protogen.Options{}, fd)
}
func apiLevelForDescriptorOpts(opts protogen.Options, fd *descpb.FileDescriptorProto) gofeaturespb.GoFeatures_APILevel {
fopts := fd.Options
if fopts == nil {
fopts = &descpb.FileOptions{}
fd.Options = fopts
}
fopts.GoPackage = proto.String("dummy/package")
// Determine the API level by querying protogen using a stub plugin request.
plugin, err := opts.New(&pluginpb.CodeGeneratorRequest{
ProtoFile: []*descpb.FileDescriptorProto{fd},
})
if err != nil {
panic(err)
}
if got, want := len(plugin.Files), 1; got != want {
panic(fmt.Sprintf("protogen returned %d plugin.Files entries, expected %d", got, want))
}
return plugin.Files[0].APILevel
}
// DefaultFileLevel returns the default go_api_flag option for given path.
func DefaultFileLevel(path string) gofeaturespb.GoFeatures_APILevel {
if path == "testonly-opaque-default-dummy.proto" {
// Magic filename to test the edition 2024+ behavior (Opaque API by
// default) from setapi_test.go
return gofeaturespb.GoFeatures_API_OPAQUE
}
fd := &descpb.FileDescriptorProto{Name: proto.String(path)}
return apiLevelForDescriptor(fd)
}
// MapGoAPIFlag maps current and old values of the go API flag to current values.
func MapGoAPIFlag(val string) (pb.GoAPI, error) {
// Old go_api_flag values are still used here in order to parse proto files from
// older /google_src/files/... paths.
// LINT.IfChange(go_api_flag_parsing)
switch val {
case "OPEN_V1":
return pb.GoAPI_OPEN_V1, nil
case "OPEN_TO_OPAQUE_HYBRID":
return pb.GoAPI_OPEN_TO_OPAQUE_HYBRID, nil
case "OPAQUE_V0":
return pb.GoAPI_OPAQUE_V0, nil
default:
return pb.GoAPI_INVALID, fmt.Errorf("invalid value: %q", val)
}
// LINT.ThenChange()
}