Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions entproto/adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const (
var (
ErrSchemaSkipped = errors.New("entproto: schema not annotated with Generate=true")
repeatedFieldLabel = descriptorpb.FieldDescriptorProto_LABEL_REPEATED
optionalFieldLabel = descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL
wktsPaths = map[string]string{
// TODO: handle more Well-Known proto types
"google.protobuf.Timestamp": "google/protobuf/timestamp.proto",
Expand Down Expand Up @@ -325,8 +326,7 @@ func (a *Adapter) toProtoMessageDescriptor(genType *gen.Type) (*descriptorpb.Des
if _, ok := f.Annotations[SkipAnnotation]; ok {
continue
}

protoField, err := toProtoFieldDescriptor(f)
protoField, err := toProtoFieldDescriptor(f, msgAnnot.EnableOptionalLabel)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -457,7 +457,7 @@ func toProtoEnumDescriptor(fld *gen.Field) (*descriptorpb.EnumDescriptorProto, e
return dp, nil
}

func toProtoFieldDescriptor(f *gen.Field) (*descriptorpb.FieldDescriptorProto, error) {
func toProtoFieldDescriptor(f *gen.Field, enableOptionalLabel bool) (*descriptorpb.FieldDescriptorProto, error) {
fieldDesc := &descriptorpb.FieldDescriptorProto{
Name: &f.Name,
}
Expand All @@ -480,7 +480,7 @@ func toProtoFieldDescriptor(f *gen.Field) (*descriptorpb.FieldDescriptorProto, e
}
return fieldDesc, nil
}
typeDetails, err := extractProtoTypeDetails(f)
typeDetails, err := extractProtoTypeDetails(f, enableOptionalLabel)
if err != nil {
return nil, err
}
Expand All @@ -490,19 +490,21 @@ func toProtoFieldDescriptor(f *gen.Field) (*descriptorpb.FieldDescriptorProto, e
}
if typeDetails.repeated {
fieldDesc.Label = &repeatedFieldLabel
} else if typeDetails.optional {
fieldDesc.Label = &optionalFieldLabel
}
return fieldDesc, nil
}

func extractProtoTypeDetails(f *gen.Field) (fieldType, error) {
func extractProtoTypeDetails(f *gen.Field, enableOptionalLabel bool) (fieldType, error) {
if f.Type.Type == field.TypeJSON {
return extractJSONDetails(f)
return extractJSONDetails(f) // repeat fields are not required for optional label.
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Corrected 'repeat' to 'repeated' for grammatical accuracy.

Suggested change
return extractJSONDetails(f) // repeat fields are not required for optional label.
return extractJSONDetails(f) // repeated fields are not required for optional label.

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment is unclear about why the enableOptionalLabel parameter is not passed to extractJSONDetails. Consider clarifying: 'JSON fields only support repeated (array) types, not optional labels.' This better explains that extractJSONDetails doesn't need the enableOptionalLabel parameter because it only handles repeated fields.

Suggested change
return extractJSONDetails(f) // repeat fields are not required for optional label.
return extractJSONDetails(f) // JSON fields only support repeated (array) types, not optional labels.

Copilot uses AI. Check for mistakes.
}
cfg, ok := typeMap[f.Type.Type]
if !ok || cfg.unsupported {
return fieldType{}, unsupportedTypeError{Type: f.Type}
}
if f.Optional {
if f.Optional && !enableOptionalLabel {
if cfg.optionalType == "" {
return fieldType{}, unsupportedTypeError{Type: f.Type}
}
Expand All @@ -518,6 +520,7 @@ func extractProtoTypeDetails(f *gen.Field) (fieldType, error) {
return fieldType{
protoType: cfg.pbType,
messageName: name,
optional: f.Optional && enableOptionalLabel,
}, nil
}

Expand Down Expand Up @@ -556,6 +559,7 @@ type fieldType struct {
messageName string
protoType descriptorpb.FieldDescriptorProto_Type
repeated bool
optional bool
}

func strptr(s string) *string {
Expand Down
9 changes: 9 additions & 0 deletions entproto/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,18 @@ func PackageName(pkg string) MessageOption {
}
}

// EnableOptionalLabel enables the optional label in the generated protobuf message instead of google/protobuf/wrappers.proto
func EnableOptionalLabel() MessageOption {
return func(msg *message) {
msg.EnableOptionalLabel = true
}
}

type message struct {
Generate bool
Package string

EnableOptionalLabel bool
}

func (m message) Name() string {
Expand Down
2 changes: 1 addition & 1 deletion entproto/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ var plural = gen.Funcs["plural"].(func(string) string)

func (a *Adapter) genMethodProtos(genType *gen.Type, m Method) (methodResources, error) {
input := &descriptorpb.DescriptorProto{}
idField, err := toProtoFieldDescriptor(genType.ID)
idField, err := toProtoFieldDescriptor(genType.ID, false)
if err != nil {
return methodResources{}, err
}
Expand Down
Loading