Skip to content

Commit

Permalink
Remove generation of unnecessary view types. (#3568)
Browse files Browse the repository at this point in the history
* Remove generation of unnecessary view types.

* Small perf tweak
  • Loading branch information
raphael authored Jul 31, 2024
1 parent 080e895 commit c211031
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 81 deletions.
41 changes: 41 additions & 0 deletions codegen/service/service_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -1189,6 +1189,9 @@ func BuildSchemeData(s *expr.SchemeExpr, m *expr.MethodExpr) *SchemeData {
// just result types - because user types make contain result types and thus may
// need to be marshalled in different ways depending on the view being used.
func collectProjectedTypes(projected, att *expr.AttributeExpr, viewspkg string, scope, viewScope *codegen.NameScope, seen map[string]*ProjectedTypeData) (data []*ProjectedTypeData, umeths []*UnionValueMethodData) {
if !hasResultType(att) {
return
}
collect := func(projected, att *expr.AttributeExpr) ([]*ProjectedTypeData, []*UnionValueMethodData) {
return collectProjectedTypes(projected, att, viewspkg, scope, viewScope, seen)
}
Expand Down Expand Up @@ -1253,6 +1256,44 @@ func collectProjectedTypes(projected, att *expr.AttributeExpr, viewspkg string,
return
}

// hasResultType returns true if the given attribute has a result type recursively.
func hasResultType(att *expr.AttributeExpr, seens ...map[string]struct{}) bool {
if _, ok := att.Type.(*expr.ResultTypeExpr); ok {
return true
}
var seen map[string]struct{}
if len(seens) > 0 {
seen = seens[0]
} else {
seen = make(map[string]struct{})
}
switch a := att.Type.(type) {
case expr.UserType:
if _, ok := seen[a.ID()]; ok {
return false
}
seen[a.ID()] = struct{}{}
return hasResultType(a.Attribute(), seen)
case *expr.Array:
return hasResultType(a.ElemType, seen)
case *expr.Map:
return hasResultType(a.KeyType, seen) || hasResultType(a.ElemType, seen)
case *expr.Object:
for _, nat := range *a {
if hasResultType(nat.Attribute, seen) {
return true
}
}
case *expr.Union:
for _, nat := range a.Values {
if hasResultType(nat.Attribute, seen) {
return true
}
}
}
return false
}

// buildProjectedType builds projected type for the given user type.
//
// viewspkg is the name of the views package
Expand Down
58 changes: 29 additions & 29 deletions codegen/service/testdata/service_code.go
Original file line number Diff line number Diff line change
Expand Up @@ -1537,15 +1537,15 @@ func newResultOneof(vres *resultwithoneoftypeviews.ResultOneofView) *ResultOneof
res := &ResultOneof{}
if vres.Result != nil {
switch actual := vres.Result.(type) {
case *resultwithoneoftypeviews.TView:
case *resultwithoneoftypeviews.T:
obj := &T{
Message: actual.Message,
}
res.Result = obj
case *resultwithoneoftypeviews.UView:
case *resultwithoneoftypeviews.U:
obj := &U{}
if actual.Item != nil {
obj.(*U).Item = transformResultwithoneoftypeviewsItemViewToItem(actual.Item)
obj.(*U).Item = transformResultwithoneoftypeviewsItemToItem(actual.Item)
}
res.Result = obj
}
Expand All @@ -1560,24 +1560,24 @@ func newResultOneofView(res *ResultOneof) *resultwithoneoftypeviews.ResultOneofV
if res.Result != nil {
switch actual := res.Result.(type) {
case *T:
obj := &resultwithoneoftypeviews.TView{
obj := &resultwithoneoftypeviews.T{
Message: actual.Message,
}
vres.Result = obj
case *U:
obj := &resultwithoneoftypeviews.UView{}
obj := &resultwithoneoftypeviews.U{}
if actual.Item != nil {
obj.(*resultwithoneoftypeviews.UView).Item = transformItemToResultwithoneoftypeviewsItemView(actual.Item)
obj.(*resultwithoneoftypeviews.U).Item = transformItemToResultwithoneoftypeviewsItem(actual.Item)
}
vres.Result = obj
}
}
return vres
}
// transformResultwithoneoftypeviewsTViewToT builds a value of type *T from a
// value of type *resultwithoneoftypeviews.TView.
func transformResultwithoneoftypeviewsTViewToT(v *resultwithoneoftypeviews.TView) *T {
// transformResultwithoneoftypeviewsTToT builds a value of type *T from a value
// of type *resultwithoneoftypeviews.T.
func transformResultwithoneoftypeviewsTToT(v *resultwithoneoftypeviews.T) *T {
if v == nil {
return nil
}
Expand All @@ -1588,23 +1588,23 @@ func transformResultwithoneoftypeviewsTViewToT(v *resultwithoneoftypeviews.TView
return res
}
// transformResultwithoneoftypeviewsUViewToU builds a value of type *U from a
// value of type *resultwithoneoftypeviews.UView.
func transformResultwithoneoftypeviewsUViewToU(v *resultwithoneoftypeviews.UView) *U {
// transformResultwithoneoftypeviewsUToU builds a value of type *U from a value
// of type *resultwithoneoftypeviews.U.
func transformResultwithoneoftypeviewsUToU(v *resultwithoneoftypeviews.U) *U {
if v == nil {
return nil
}
res := &U{}
if v.Item != nil {
res.Item = transformResultwithoneoftypeviewsItemViewToItem(v.Item)
res.Item = transformResultwithoneoftypeviewsItemToItem(v.Item)
}
return res
}
// transformResultwithoneoftypeviewsItemViewToItem builds a value of type *Item
// from a value of type *resultwithoneoftypeviews.ItemView.
func transformResultwithoneoftypeviewsItemViewToItem(v *resultwithoneoftypeviews.ItemView) *Item {
// transformResultwithoneoftypeviewsItemToItem builds a value of type *Item
// from a value of type *resultwithoneoftypeviews.Item.
func transformResultwithoneoftypeviewsItemToItem(v *resultwithoneoftypeviews.Item) *Item {
if v == nil {
return nil
}
Expand All @@ -1615,40 +1615,40 @@ func transformResultwithoneoftypeviewsItemViewToItem(v *resultwithoneoftypeviews
return res
}
// transformTToResultwithoneoftypeviewsTView builds a value of type
// *resultwithoneoftypeviews.TView from a value of type *T.
func transformTToResultwithoneoftypeviewsTView(v *T) *resultwithoneoftypeviews.TView {
// transformTToResultwithoneoftypeviewsT builds a value of type
// *resultwithoneoftypeviews.T from a value of type *T.
func transformTToResultwithoneoftypeviewsT(v *T) *resultwithoneoftypeviews.T {
if v == nil {
return nil
}
res := &resultwithoneoftypeviews.TView{
res := &resultwithoneoftypeviews.T{
Message: v.Message,
}
return res
}
// transformUToResultwithoneoftypeviewsUView builds a value of type
// *resultwithoneoftypeviews.UView from a value of type *U.
func transformUToResultwithoneoftypeviewsUView(v *U) *resultwithoneoftypeviews.UView {
// transformUToResultwithoneoftypeviewsU builds a value of type
// *resultwithoneoftypeviews.U from a value of type *U.
func transformUToResultwithoneoftypeviewsU(v *U) *resultwithoneoftypeviews.U {
if v == nil {
return nil
}
res := &resultwithoneoftypeviews.UView{}
res := &resultwithoneoftypeviews.U{}
if v.Item != nil {
res.Item = transformItemToResultwithoneoftypeviewsItemView(v.Item)
res.Item = transformItemToResultwithoneoftypeviewsItem(v.Item)
}
return res
}
// transformItemToResultwithoneoftypeviewsItemView builds a value of type
// *resultwithoneoftypeviews.ItemView from a value of type *Item.
func transformItemToResultwithoneoftypeviewsItemView(v *Item) *resultwithoneoftypeviews.ItemView {
// transformItemToResultwithoneoftypeviewsItem builds a value of type
// *resultwithoneoftypeviews.Item from a value of type *Item.
func transformItemToResultwithoneoftypeviewsItem(v *Item) *resultwithoneoftypeviews.Item {
if v == nil {
return nil
}
res := &resultwithoneoftypeviews.ItemView{
res := &resultwithoneoftypeviews.Item{
A: v.A,
}
Expand Down
54 changes: 5 additions & 49 deletions codegen/service/testdata/views_code.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,15 +175,10 @@ type ResultType struct {
// ResultTypeView is a type that runs validations on a projected type.
type ResultTypeView struct {
A *UserTypeView
A *UserType
B *string
}
// UserTypeView is a type that runs validations on a projected type.
type UserTypeView struct {
A *string
}
var (
// ResultTypeMap is a map indexing the attribute names of ResultType by view
// name.
Expand Down Expand Up @@ -229,12 +224,6 @@ func ValidateResultTypeViewTiny(result *ResultTypeView) (err error) {
}
return
}
// ValidateUserTypeView runs the validations defined on UserTypeView.
func ValidateUserTypeView(result *UserTypeView) (err error) {
return
}
`

const ResultWithResultTypeCode = `// RT is the viewed result type that is projected based on a view.
Expand All @@ -255,19 +244,14 @@ type RTView struct {
// RT2View is a type that runs validations on a projected type.
type RT2View struct {
C *string
D *UserTypeView
D *UserType
E *string
}
// UserTypeView is a type that runs validations on a projected type.
type UserTypeView struct {
P *string
}
// RT3View is a type that runs validations on a projected type.
type RT3View struct {
X []string
Y map[int]*UserTypeView
Y map[int]*UserType
Z *string
}
Expand Down Expand Up @@ -391,12 +375,6 @@ func ValidateRT2ViewTiny(result *RT2View) (err error) {
return
}
// ValidateUserTypeView runs the validations defined on UserTypeView.
func ValidateUserTypeView(result *UserTypeView) (err error) {
return
}
// ValidateRT3View runs the validations defined on RT3View using the "default"
// view.
func ValidateRT3View(result *RT3View) (err error) {
Expand Down Expand Up @@ -763,12 +741,9 @@ type Result struct {
// ResultView is a type that runs validations on a projected type.
type ResultView struct {
T []UserTypeView
T []UserType
}
// UserTypeView is a type that runs validations on a projected type.
type UserTypeView string
var (
// ResultMap is a map indexing the attribute names of Result by view name.
ResultMap = map[string][]string{
Expand Down Expand Up @@ -799,14 +774,6 @@ func ValidateResultView(result *ResultView) (err error) {
}
return
}
// ValidateUserTypeView runs the validations defined on UserTypeView.
func ValidateUserTypeView(result UserTypeView) (err error) {
if !(string(result) == "a" || string(result) == "b") {
err = goa.MergeErrors(err, goa.InvalidEnumValueError("result", string(result), []any{"a", "b"}))
}
return
}
`

const ResultWithPkgPathCode = `// RT is the viewed result type that is projected based on a view.
Expand All @@ -819,12 +786,7 @@ type RT struct {
// RTView is a type that runs validations on a projected type.
type RTView struct {
A *UserTypeView
}
// UserTypeView is a type that runs validations on a projected type.
type UserTypeView struct {
A *string
A *UserType
}
var (
Expand Down Expand Up @@ -853,10 +815,4 @@ func ValidateRTView(result *RTView) (err error) {
return
}
// ValidateUserTypeView runs the validations defined on UserTypeView.
func ValidateUserTypeView(result *UserTypeView) (err error) {
return
}
`
6 changes: 3 additions & 3 deletions http/codegen/client_body_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ const ExplicitBodyUserResultMultipleViewsInitCode = `// NewMethodExplicitBodyUse
// "MethodExplicitBodyUserResultMultipleView" endpoint result from a HTTP "OK"
// response.
func NewMethodExplicitBodyUserResultMultipleViewResulttypemultipleviewsOK(body *MethodExplicitBodyUserResultMultipleViewResponseBody, c *string) *serviceexplicitbodyuserresultmultipleviewviews.ResulttypemultipleviewsView {
v := &serviceexplicitbodyuserresultmultipleviewviews.UserTypeView{
v := &serviceexplicitbodyuserresultmultipleviewviews.UserType{
X: body.X,
Y: body.Y,
}
Expand All @@ -256,7 +256,7 @@ const ExplicitBodyObjectInitCode = `// NewMethodExplicitBodyUserResultObjectResu
func NewMethodExplicitBodyUserResultObjectResulttypeOK(body *MethodExplicitBodyUserResultObjectResponseBody, c *string, b *string) *serviceexplicitbodyuserresultobjectviews.ResulttypeView {
v := &serviceexplicitbodyuserresultobjectviews.ResulttypeView{}
if body.A != nil {
v.A = unmarshalUserTypeResponseBodyToServiceexplicitbodyuserresultobjectviewsUserTypeView(body.A)
v.A = unmarshalUserTypeResponseBodyToServiceexplicitbodyuserresultobjectviewsUserType(body.A)
}
v.C = c
v.B = b
Expand All @@ -272,7 +272,7 @@ const ExplicitBodyObjectViewsInitCode = `// NewMethodExplicitBodyUserResultObjec
func NewMethodExplicitBodyUserResultObjectMultipleViewResulttypemultipleviewsOK(body *MethodExplicitBodyUserResultObjectMultipleViewResponseBody, c *string) *serviceexplicitbodyuserresultobjectmultipleviewviews.ResulttypemultipleviewsView {
v := &serviceexplicitbodyuserresultobjectmultipleviewviews.ResulttypemultipleviewsView{}
if body.A != nil {
v.A = unmarshalUserTypeResponseBodyToServiceexplicitbodyuserresultobjectmultipleviewviewsUserTypeView(body.A)
v.A = unmarshalUserTypeResponseBodyToServiceexplicitbodyuserresultobjectmultipleviewviewsUserType(body.A)
}
v.C = c
Expand Down

0 comments on commit c211031

Please sign in to comment.