Skip to content

Commit

Permalink
Only inline inlined types; remove inline ignore config
Browse files Browse the repository at this point in the history
  • Loading branch information
tenstad committed Mar 24, 2022
1 parent 9b59535 commit e95503b
Show file tree
Hide file tree
Showing 10 changed files with 125 additions and 79 deletions.
5 changes: 1 addition & 4 deletions config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,9 @@ processor:
- "(Elasticsearch|Kibana|ApmServer|Reconciler)Status$"
- "ElasticsearchSettings$"
- "Associa(ted|tor|tionStatus|tionConf)$"
embeddedIgnoreTypes:
- "TypeMeta$"
- "ObjectMeta$"
- "ListMeta$"
ignoreFields:
- "status$"
- "TypeMeta$"

render:
kubernetesVersion: 1.22
1 change: 0 additions & 1 deletion config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ type Config struct {
type ProcessorConfig struct {
MaxDepth int `json:"maxDepth"`
IgnoreTypes []string `json:"ignoreTypes"`
EmbeddedIgnoreTypes []string `json:"embeddedIgnoreTypes"`
IgnoreFields []string `json:"ignoreFields"`
IgnoreGroupVersions []string `json:"ignoreGroupVersions"`
}
Expand Down
22 changes: 0 additions & 22 deletions processor/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ func compileConfig(conf *config.Config) (cc *compiledConfig, err error) {

cc = &compiledConfig{
ignoreTypes: make([]*regexp.Regexp, len(conf.Processor.IgnoreTypes)),
embeddedIgnoreTypes: make([]*regexp.Regexp, len(conf.Processor.EmbeddedIgnoreTypes)),
ignoreFields: make([]*regexp.Regexp, len(conf.Processor.IgnoreFields)),
ignoreGroupVersions: make([]*regexp.Regexp, len(conf.Processor.IgnoreGroupVersions)),
}
Expand All @@ -41,12 +40,6 @@ func compileConfig(conf *config.Config) (cc *compiledConfig, err error) {
}
}

for i, f := range conf.Processor.EmbeddedIgnoreTypes {
if cc.embeddedIgnoreTypes[i], err = regexp.Compile(f); err != nil {
return nil, fmt.Errorf("failed to compile type regex '%s': %w", f, err)
}
}

for i, f := range conf.Processor.IgnoreFields {
if cc.ignoreFields[i], err = regexp.Compile(f); err != nil {
return nil, fmt.Errorf("failed to compile field regex '%s': %w", f, err)
Expand All @@ -64,7 +57,6 @@ func compileConfig(conf *config.Config) (cc *compiledConfig, err error) {

type compiledConfig struct {
ignoreTypes []*regexp.Regexp
embeddedIgnoreTypes []*regexp.Regexp
ignoreFields []*regexp.Regexp
ignoreGroupVersions []*regexp.Regexp
}
Expand Down Expand Up @@ -97,20 +89,6 @@ func (cc *compiledConfig) shouldIgnoreType(fqn string) bool {
return false
}

func (cc *compiledConfig) shouldIgnoreEmbeddedType(fqn string) bool {
if cc == nil {
return false
}

for _, re := range cc.embeddedIgnoreTypes {
if re.MatchString(fqn) {
return true
}
}

return false
}

func (cc *compiledConfig) shouldIgnoreField(typeName, fieldName string) bool {
if cc == nil {
return false
Expand Down
6 changes: 0 additions & 6 deletions processor/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ func TestCompiledConfig(t *testing.T) {
conf := &config.Config{
Processor: config.ProcessorConfig{
IgnoreTypes: []string{"typex$"},
EmbeddedIgnoreTypes: []string{"typex$"},
IgnoreFields: []string{`mytype\.Fieldy$`},
IgnoreGroupVersions: []string{"groupz/v1$"},
},
Expand All @@ -25,11 +24,6 @@ func TestCompiledConfig(t *testing.T) {
require.False(t, cc.shouldIgnoreType("typexyz"))
})

t.Run("imbeddedIgnoreTypes", func(t *testing.T) {
require.True(t, cc.shouldIgnoreEmbeddedType("mytypex"))
require.False(t, cc.shouldIgnoreEmbeddedType("typexyz"))
})

t.Run("ignoreField", func(t *testing.T) {
require.True(t, cc.shouldIgnoreField("mytype", "Fieldy"))
require.False(t, cc.shouldIgnoreField("mytype", "Fieldyz"))
Expand Down
28 changes: 7 additions & 21 deletions processor/processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func Process(config *config.Config) ([]types.GroupVersionDetails, error) {
return nil, fmt.Errorf("failed to find API types in directory %s:%w", config.SourcePath, err)
}

p.types.InlineEmbeddedFields()
p.types.InlineTypes()

// collect references between types
for typeName, refs := range p.references {
Expand Down Expand Up @@ -89,8 +89,6 @@ func Process(config *config.Config) ([]types.GroupVersionDetails, error) {
for name, t := range gvi.types {
key := types.Key(t)

// Skip types that are processed due to beeing embedded and inlined
// in parent type, but not rendered themselves.
if p.shouldIgnoreType(key) {
zap.S().Debugw("Skipping excluded type", "type", name)
continue
Expand Down Expand Up @@ -274,11 +272,6 @@ func (p *processor) processType(pkg *loader.Package, info *markers.TypeInfo, dep
Doc: info.Doc,
}

if p.shouldIgnoreType(types.Key(typeDef)) && p.shouldIgnoreEmbeddedType(types.Key(typeDef)) {
zap.S().Debugw("Skipping excluded type", "type", typeDef.String())
return nil
}

// if the field list is non-empty, this is a struct
if len(info.Fields) > 0 {
typeDef.Kind = types.StructKind
Expand Down Expand Up @@ -335,17 +328,14 @@ func (p *processor) processStructFields(parentType *types.Type, pkg *loader.Pack
continue
}

if fieldDef.Embedded && fieldDef.Name == "" {
fieldDef.Name = fieldDef.Type.Name
}

if fieldDef.Embedded {
if p.shouldIgnoreEmbeddedType(types.Key(fieldDef.Type)) {
zap.S().Debugw("Skipping excluded embedded type",
"type", parentType.String(), "embeddedType", fieldDef.Type.Name)
continue
fieldDef.Inlined = fieldDef.Name == ""
if fieldDef.Name == "" {
fieldDef.Name = fieldDef.Type.Name
}
} else if p.shouldIgnoreField(parentTypeKey, fieldDef.Name) {
}

if p.shouldIgnoreField(parentTypeKey, fieldDef.Name) {
zap.S().Debugw("Skipping excluded field", "type", parentType.String(), "field", fieldDef.Name)
continue
}
Expand All @@ -366,10 +356,6 @@ func (p *processor) loadType(pkg *loader.Package, t gotypes.Type, depth int) *ty
}

typeDef := mkType(pkg, t)
if p.shouldIgnoreType(types.Key(typeDef)) && p.shouldIgnoreEmbeddedType(types.Key(typeDef)) {
zap.S().Debugw("Skipping excluded type", "type", t.String())
return nil
}

zap.S().Debugw("Load", "package", typeDef.Package, "name", typeDef.Name)

Expand Down
88 changes: 88 additions & 0 deletions test/api/v1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 1 addition & 4 deletions test/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,9 @@ processor:
- "GVK"
ignoreTypes:
- "Embedded[0-9]$"
embeddedIgnoreTypes:
- "TypeMeta$"
- "ObjectMeta$"
- "ListMeta$"
ignoreFields:
- "status$"
- "TypeMeta$"

render:
kubernetesVersion: 1.22
4 changes: 4 additions & 0 deletions test/expected.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ Guestbook is the Schema for the guestbooks API.
| Field | Description
| *`apiVersion`* __string__ | `webapp.test.k8s.elastic.co/v1`
| *`kind`* __string__ | `Guestbook`
| *`metadata`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#objectmeta-v1-meta[$$ObjectMeta$$]__ | Refer to Kubernetes API documentation for fields of `metadata`.

| *`spec`* __xref:{anchor_prefix}-github-com-elastic-crd-ref-docs-api-v1-guestbookspec[$$GuestbookSpec$$]__ |
|===

Expand Down Expand Up @@ -103,6 +105,8 @@ GuestbookList contains a list of Guestbook.
| Field | Description
| *`apiVersion`* __string__ | `webapp.test.k8s.elastic.co/v1`
| *`kind`* __string__ | `GuestbookList`
| *`metadata`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#listmeta-v1-meta[$$ListMeta$$]__ | Refer to Kubernetes API documentation for fields of `metadata`.

| *`items`* __xref:{anchor_prefix}-github-com-elastic-crd-ref-docs-api-v1-guestbook[$$Guestbook$$] array__ |
|===

Expand Down
2 changes: 2 additions & 0 deletions test/expected.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ _Appears in:_
| --- | --- |
| `apiVersion` _string_ | `webapp.test.k8s.elastic.co/v1`
| `kind` _string_ | `Guestbook`
| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. |
| `spec` _[GuestbookSpec](#guestbookspec)_ | |


Expand Down Expand Up @@ -90,6 +91,7 @@ GuestbookList contains a list of Guestbook.
| --- | --- |
| `apiVersion` _string_ | `webapp.test.k8s.elastic.co/v1`
| `kind` _string_ | `GuestbookList`
| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. |
| `items` _[Guestbook](#guestbook) array_ | |


Expand Down
43 changes: 22 additions & 21 deletions types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,9 +211,9 @@ func (t *Type) SortedReferences() []*Type {
return t.References
}

func (t *Type) ContainsEmbeddedField() bool {
func (t *Type) ContainsInlinedTypes() bool {
for _, f := range t.Members() {
if f.Embedded {
if f.Inlined {
return true
}
}
Expand All @@ -223,23 +223,23 @@ func (t *Type) ContainsEmbeddedField() bool {
// TypeMap is a map of Type elements
type TypeMap map[string]*Type

func (types TypeMap) InlineEmbeddedFields() {
// If C is embedded in B, and B is embedded in A; the fields in C are
// inlined in B before the fields in B is inlined in A. The ideal order of
// iterating and embedding types is NOT known. Worst-case, only one type's
func (types TypeMap) InlineTypes() {
// If C is inlined in B, and B is inlined in A; the fields of C are copied
// into B before the fields of B is copied into A. The ideal order of
// iterating and inlining fields is NOT known. Worst-case, only one type's
// fields are inlined in its parent type in each iteration.
maxDepth := 100
var numEmbeddedFields int
var numTypesToBeInlined int
for iteration := 0; iteration < maxDepth; iteration++ {
numEmbeddedFields = 0
numTypesToBeInlined = 0
for _, t := range types {
// By iterating backwards, it is safe to delete field at current index
// and append the embedded fields it contains.
// and copy the fields of the inlined type.
for i := len(t.Fields) - 1; i >= 0; i-- {
if !t.Fields[i].Embedded {
if !t.Fields[i].Inlined {
continue
}
numEmbeddedFields += 1
numTypesToBeInlined += 1

embeddedType, ok := types[Key(t.Fields[i].Type)]
if !ok {
Expand All @@ -248,37 +248,38 @@ func (types TypeMap) InlineEmbeddedFields() {
continue
}

// Only inline embedded type's fields if the embedded type
// itself has no embedded types yet to be inlined.
if !embeddedType.ContainsEmbeddedField() {
// Only inline type's fields if the inlined type itself has no
// types yet to be inlined.
if !embeddedType.ContainsInlinedTypes() {
zap.S().Debugw("Inlining embedded type", "type", t,
"embeddedType", t.Fields[i].Type)
t.Fields.inlineEmbedded(i, embeddedType)
t.Fields.inlineType(i, embeddedType)
}
}
}
if numEmbeddedFields == 0 {
if numTypesToBeInlined == 0 {
return
}
}
zap.S().Warnw("Failed to inline all embedded types", "remaining", numEmbeddedFields)
zap.S().Warnw("Failed to inline all inlined types", "remaining", numTypesToBeInlined)
}

// Field describes a field in a struct.
type Field struct {
Name string
Embedded bool
Inlined bool
Doc string
Type *Type
}

type Fields []*Field

// inlineEmbedded replaces field at index i with the fields of provided type.
func (fields *Fields) inlineEmbedded(i int, embedded *Type) {
new := make([]*Field, 0, len(*fields)+len(embedded.Fields)-1)
// inlineType replaces field at index i with the fields of inlined type.
func (fields *Fields) inlineType(i int, inlined *Type) {
new := make([]*Field, 0, len(*fields)+len(inlined.Fields)-1)
new = append(new, (*fields)[:i]...)
new = append(new, embedded.Fields...)
new = append(new, inlined.Fields...)
*fields = append(new, (*fields)[i+1:]...)
}

Expand Down

0 comments on commit e95503b

Please sign in to comment.