Skip to content

Commit

Permalink
feat: support generate nested struct for slim template (#101)
Browse files Browse the repository at this point in the history
  • Loading branch information
FGYFFFF authored Jan 9, 2024
1 parent 0efea85 commit 6f0023e
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 10 deletions.
35 changes: 34 additions & 1 deletion args.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (

"github.com/cloudwego/thriftgo/generator"
"github.com/cloudwego/thriftgo/generator/backend"
"github.com/cloudwego/thriftgo/generator/golang"
"github.com/cloudwego/thriftgo/plugin"
)

Expand Down Expand Up @@ -99,7 +100,12 @@ func (a *Arguments) Targets() (specs []*generator.LangSpec, err error) {
if err != nil {
return nil, err
}

opts, err := a.checkOptions(desc.Options)
if err != nil {
return nil, err
}
// checkOptions may modify the content of the options
desc.Options = opts
spec := &generator.LangSpec{
Language: desc.Name,
Options: desc.Options,
Expand All @@ -109,6 +115,33 @@ func (a *Arguments) Targets() (specs []*generator.LangSpec, err error) {
return
}

// checkOptions used to validate the command parameters.
func (a *Arguments) checkOptions(opts []plugin.Option) ([]plugin.Option, error) {
params := plugin.Pack(opts)
cu := golang.NewCodeUtils(backend.DummyLogFunc())
cu.HandleOptions(params)
if cu.Features().EnableNestedStruct {
// In nested mode, if template is not 'slim', it is automatically converted to slim
if cu.Template() != "slim" {
found := false
for _, opt := range opts {
if opt.Name == "template" {
log.Printf("[WARN] EnableNestedStruct is only available under the \"slim\" template, so adapt the template to \"slim\"")
opt.Desc = "slim"
found = true
break
}
}
if !found {
log.Printf("[WARN] EnableNestedStruct is only available under the \"slim\" template, so adapt the template to \"slim\"")
opts = append(opts, plugin.Option{Name: "template", Desc: "slim"})
}

}
}
return opts, nil
}

// MakeLogFunc creates logging functions according to command line flags.
func (a *Arguments) MakeLogFunc() backend.LogFunc {
logs := backend.DummyLogFunc()
Expand Down
12 changes: 6 additions & 6 deletions generator/golang/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,11 @@ type Features struct {
CodeRef bool `code_ref:"Genenerate code ref by given idl-ref.yaml"`
KeepCodeRefName bool `keep_code_ref_name:"Genenerate code ref but still keep file name."`
TrimIDL bool `trim_idl:"Simplify IDL to the most concise form before generating code."`

JSONStringer bool `json_stringer:"Generate the JSON marshal method in String() method."`

WithFieldMask bool `with_field_mask:"Support field-mask for generated code."`
FieldMaskHalfway bool `field_mask_halfway:"Support set field-mask on not-root struct."`
FieldMaskZeroRequired bool `field_mask_zero_required:"Write zero value instead of current value for required fields filtered by fieldmask."`
EnableNestedStruct bool `enable_nested_struct:"Generate nested field when 'thrift.nested=\"true\"' annotation is set to field, valid only in 'slim template'"`
JSONStringer bool `json_stringer:"Generate the JSON marshal method in String() method."`
WithFieldMask bool `with_field_mask:"Support field-mask for generated code."`
FieldMaskHalfway bool `field_mask_halfway:"Support set field-mask on not-root struct."`
FieldMaskZeroRequired bool `field_mask_zero_required:"Write zero value instead of current value for required fields filtered by fieldmask."`
}

var defaultFeatures = Features{
Expand Down Expand Up @@ -95,6 +94,7 @@ var defaultFeatures = Features{
WithFieldMask: false,
FieldMaskHalfway: false,
FieldMaskZeroRequired: false,
EnableNestedStruct: false,
}

type param struct {
Expand Down
6 changes: 6 additions & 0 deletions generator/golang/scope.go
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,7 @@ type Field struct {
setter Name
isset Name
deepEqual Name
isNested bool
}

// GoName returns the name in go code of the field.
Expand Down Expand Up @@ -555,6 +556,11 @@ func (f *Field) DeepEqual() Name {
return f.deepEqual
}

// IsNested returns whether the field is a nested type.
func (f *Field) IsNested() bool {
return f.isNested
}

// StructLike is a wrapper for the parser.StructLike.
type StructLike struct {
*parser.StructLike
Expand Down
45 changes: 43 additions & 2 deletions generator/golang/scope_internal.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,12 @@ import (
"github.com/cloudwego/thriftgo/pkg/namespace"
)

// A prefix to denote synthesized identifiers.
const prefix = "$"
const (
// A prefix to denote synthesized identifiers.
prefix = "$"
// nestedAnnotation is to denote the field is nested type.
nestedAnnotation = "thrift.nested"
)

func _p(id string) string {
return prefix + id
Expand Down Expand Up @@ -321,6 +325,15 @@ func (s *Scope) buildStructLike(cu *CodeUtils, v *parser.StructLike, usedName ..
// reserve method names
for _, f := range v.Fields {
fn := s.identify(cu, f.Name)
if cu.Features().EnableNestedStruct && isNestedField(f) {
// EnableNestedStruct, the type name needs to be used when retrieving the value for getter&setter
fn = s.identify(cu, f.Type.Name)
if strings.Contains(fn, ".") {
fns := strings.Split(fn, ".")
fn = s.identify(cu, fns[len(fns)-1])
}
}

st.scope.Add("Get"+fn, _p("get:"+f.Name))
if cu.Features().GenerateSetter {
st.scope.Add("Set"+fn, _p("set:"+f.Name))
Expand All @@ -339,6 +352,10 @@ func (s *Scope) buildStructLike(cu *CodeUtils, v *parser.StructLike, usedName ..
// field names
for _, f := range v.Fields {
fn := s.identify(cu, f.Name)
isNested := false
if cu.Features().EnableNestedStruct && isNestedField(f) {
isNested = true
}
fn = st.scope.Add(fn, f.Name)
id := id2str(f.ID)
st.fields = append(st.fields, &Field{
Expand All @@ -350,6 +367,7 @@ func (s *Scope) buildStructLike(cu *CodeUtils, v *parser.StructLike, usedName ..
setter: Name(st.scope.Get(_p("set:" + f.Name))),
isset: Name(st.scope.Get(_p("isset:" + f.Name))),
deepEqual: Name(st.scope.Get(_p("deepequal:" + id))),
isNested: isNested,
})
}

Expand Down Expand Up @@ -401,6 +419,18 @@ func (s *Scope) resolveTypesAndValues(cu *CodeUtils) {
for f := range ff {
v := f.Field
f.typeName = ensureType(resolver.ResolveFieldTypeName(v))
// This is used to set the real field name for nested struct, ex.
// type T struct {
// *Nested
// }
if cu.Features().EnableNestedStruct && isNestedField(f.Field) {
name := f.typeName.Deref().String()
if strings.Contains(name, ".") {
names := strings.Split(name, ".")
name = names[len(names)-1]
}
f.name = Name(name)
}
f.frugalTypeName = ensureType(frugalResolver.ResolveFrugalTypeName(v.Type))
f.defaultTypeName = ensureType(resolver.GetDefaultValueTypeName(v))
if f.IsSetDefault() {
Expand Down Expand Up @@ -450,3 +480,14 @@ func (s *Scope) resolveTypesAndValues(cu *CodeUtils) {
}
}
}

func isNestedField(f *parser.Field) bool {
annos := f.Annotations.Get(nestedAnnotation)
if len(annos) == 0 {
return false
}
if strings.EqualFold(annos[0], "true") {
return true
}
return false
}
6 changes: 5 additions & 1 deletion generator/golang/templates/slim/slim.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,11 @@ type {{$TypeName}} struct {
{{- if and Features.ReserveComments .ReservedComments}}
{{.ReservedComments}}
{{- end}}
{{(.GoName)}} {{.GoTypeName}} {{GenFieldTags . (InsertionPoint $.Category $.Name .Name "tag")}}
{{- if .IsNested}}
{{.GoTypeName}} {{GenFieldTags . (InsertionPoint $.Category $.Name .Name "tag")}}
{{else}}
{{(.GoName)}} {{.GoTypeName}} {{GenFieldTags . (InsertionPoint $.Category $.Name .Name "tag")}}
{{- end}}
{{- end}}
{{- if Features.KeepUnknownFields}}
{{- UseStdLibrary "unknown"}}
Expand Down

0 comments on commit 6f0023e

Please sign in to comment.