package protodesc
import (
"fmt"
"os"
"sync"
"google.golang.org/protobuf/internal/editiondefaults"
"google.golang.org/protobuf/internal/filedesc"
"google.golang.org/protobuf/internal/genid"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/types/descriptorpb"
"google.golang.org/protobuf/types/gofeaturespb"
)
var defaults = &descriptorpb .FeatureSetDefaults {}
var defaultsCacheMu sync .Mutex
var defaultsCache = make (map [filedesc .Edition ]*descriptorpb .FeatureSet )
func init() {
err := proto .Unmarshal (editiondefaults .Defaults , defaults )
if err != nil {
fmt .Fprintf (os .Stderr , "unmarshal editions defaults: %v\n" , err )
os .Exit (1 )
}
}
func fromEditionProto(epb descriptorpb .Edition ) filedesc .Edition {
return filedesc .Edition (epb )
}
func toEditionProto(ed filedesc .Edition ) descriptorpb .Edition {
switch ed {
case filedesc .EditionUnknown :
return descriptorpb .Edition_EDITION_UNKNOWN
case filedesc .EditionProto2 :
return descriptorpb .Edition_EDITION_PROTO2
case filedesc .EditionProto3 :
return descriptorpb .Edition_EDITION_PROTO3
case filedesc .Edition2023 :
return descriptorpb .Edition_EDITION_2023
case filedesc .Edition2024 :
return descriptorpb .Edition_EDITION_2024
default :
panic (fmt .Sprintf ("unknown value for edition: %v" , ed ))
}
}
func getFeatureSetFor(ed filedesc .Edition ) *descriptorpb .FeatureSet {
defaultsCacheMu .Lock ()
defer defaultsCacheMu .Unlock ()
if def , ok := defaultsCache [ed ]; ok {
return def
}
edpb := toEditionProto (ed )
if defaults .GetMinimumEdition () > edpb || defaults .GetMaximumEdition () < edpb {
fmt .Fprintf (os .Stderr , "internal error: unsupported edition %v (did you forget to update the embedded defaults (i.e. the bootstrap descriptor proto)?)\n" , edpb )
os .Exit (1 )
}
fsed := defaults .GetDefaults ()[0 ]
for _ , def := range defaults .GetDefaults () {
if def .GetEdition () <= edpb {
fsed = def
} else {
break
}
}
fs := proto .Clone (fsed .GetFixedFeatures ()).(*descriptorpb .FeatureSet )
proto .Merge (fs , fsed .GetOverridableFeatures ())
defaultsCache [ed ] = fs
return fs
}
func mergeEditionFeatures(parentDesc protoreflect .Descriptor , child *descriptorpb .FeatureSet ) filedesc .EditionFeatures {
var parentFS filedesc .EditionFeatures
switch p := parentDesc .(type ) {
case *filedesc .File :
parentFS = p .L1 .EditionFeatures
case *filedesc .Message :
parentFS = p .L1 .EditionFeatures
default :
panic (fmt .Sprintf ("unknown parent type %T" , parentDesc ))
}
if child == nil {
return parentFS
}
if fp := child .FieldPresence ; fp != nil {
parentFS .IsFieldPresence = *fp == descriptorpb .FeatureSet_LEGACY_REQUIRED ||
*fp == descriptorpb .FeatureSet_EXPLICIT
parentFS .IsLegacyRequired = *fp == descriptorpb .FeatureSet_LEGACY_REQUIRED
}
if et := child .EnumType ; et != nil {
parentFS .IsOpenEnum = *et == descriptorpb .FeatureSet_OPEN
}
if rfe := child .RepeatedFieldEncoding ; rfe != nil {
parentFS .IsPacked = *rfe == descriptorpb .FeatureSet_PACKED
}
if utf8val := child .Utf8Validation ; utf8val != nil {
parentFS .IsUTF8Validated = *utf8val == descriptorpb .FeatureSet_VERIFY
}
if me := child .MessageEncoding ; me != nil {
parentFS .IsDelimitedEncoded = *me == descriptorpb .FeatureSet_DELIMITED
}
if jf := child .JsonFormat ; jf != nil {
parentFS .IsJSONCompliant = *jf == descriptorpb .FeatureSet_ALLOW
}
goFeatures := child .ProtoReflect ().Get (gofeaturespb .E_Go .TypeDescriptor ())
if !goFeatures .IsValid () {
return parentFS
}
gf , ok := goFeatures .Interface ().(protoreflect .Message )
if !ok {
return parentFS
}
fields := gf .Descriptor ().Fields ()
if fd := fields .ByNumber (genid .GoFeatures_LegacyUnmarshalJsonEnum_field_number ); fd != nil &&
!fd .IsList () &&
fd .Kind () == protoreflect .BoolKind &&
gf .Has (fd ) {
parentFS .GenerateLegacyUnmarshalJSON = gf .Get (fd ).Bool ()
}
if fd := fields .ByNumber (genid .GoFeatures_StripEnumPrefix_field_number ); fd != nil &&
!fd .IsList () &&
fd .Kind () == protoreflect .EnumKind &&
gf .Has (fd ) {
parentFS .StripEnumPrefix = int (gf .Get (fd ).Enum ())
}
if fd := fields .ByNumber (genid .GoFeatures_ApiLevel_field_number ); fd != nil &&
!fd .IsList () &&
fd .Kind () == protoreflect .EnumKind &&
gf .Has (fd ) {
parentFS .APILevel = int (gf .Get (fd ).Enum ())
}
return parentFS
}
func initFileDescFromFeatureSet(fd *filedesc .File , fs *descriptorpb .FeatureSet ) {
dfs := getFeatureSetFor (fd .L1 .Edition )
fd .L1 .EditionFeatures = mergeEditionFeatures (fd , dfs )
fd .L1 .EditionFeatures = mergeEditionFeatures (fd , fs )
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .