Skip to content

Commit

Permalink
Support constant values of enum aliased types (#79)
Browse files Browse the repository at this point in the history
This adds support for extracting information about constant values
of alias types used to simulate enumerations.
  • Loading branch information
liorokman committed Apr 13, 2024
1 parent 284491a commit ea9fcaa
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 22 deletions.
39 changes: 39 additions & 0 deletions processor/processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ package processor

import (
"fmt"
"go/ast"
"go/token"
gotypes "go/types"
"regexp"
"sort"
Expand Down Expand Up @@ -327,6 +329,9 @@ func (p *processor) processType(pkg *loader.Package, parentType *types.Type, t g
if info != nil {
underlying = pkg.TypesInfo.TypeOf(info.RawSpec.Type)
}
if underlying.String() == "string" {
typeDef.EnumValues = lookupConstantValuesForAliasedType(pkg, typeDef.Name)
}
typeDef.UnderlyingType = p.processType(pkg, typeDef, underlying, depth+1)
p.addReference(typeDef, typeDef.UnderlyingType)

Expand Down Expand Up @@ -570,3 +575,37 @@ func (p *processor) parseMarkers() {
}
}
}

func lookupConstantValuesForAliasedType(pkg *loader.Package, aliasTypeName string) []types.EnumValue {
values := []types.EnumValue{}
for _, file := range pkg.Syntax {
for _, decl := range file.Decls {
node, ok := decl.(*ast.GenDecl)
if !ok || node.Tok != token.CONST {
continue
}
for _, spec := range node.Specs {
// look for constant declaration
v, ok := spec.(*ast.ValueSpec)
if !ok {
continue
}
// value type must match the alias type name and have exactly one value
if id, ok := v.Type.(*ast.Ident); !ok || id.String() != aliasTypeName || len(v.Values) != 1 {
continue
}
// convert to a basic type to access to the value
b, ok := v.Values[0].(*ast.BasicLit)
if !ok {
continue
}
values = append(values, types.EnumValue{
// remove the '"' signs from the start and end of the value
Name: b.Value[1 : len(b.Value)-1],
Doc: v.Doc.Text(),
})
}
}
}
return values
}
8 changes: 8 additions & 0 deletions templates/markdown/type.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,13 @@ _Appears in:_

{{ end -}}

{{ if $type.EnumValues -}}
| Field | Description |
{{ range $type.EnumValues -}}
| `{{ .Name }}` | {{ markdownRenderFieldDoc .Doc }} |
{{ end -}}
{{ end -}}


{{- end -}}
{{- end -}}
12 changes: 12 additions & 0 deletions test/api/v1/guestbook_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,20 @@ type GuestbookSpec struct {
// CertificateRef is a reference to a secret containing a certificate
CertificateRef gwapiv1b1.SecretObjectReference `json:"certificateRef"`
String common.CommonString `json:"str"`
// Enumeration is an example of an aliased enumeration type
Enumeration MyEnum `json:"enum"`
}

// +kubebuilder:validation:Enum=MyFirstValue;MySecondValue
type MyEnum string

const (
// MyFirstValue is an interesting value to use
MyFirstValue MyEnum = "MyFirstValue"
// MySecondValue is what you use when you can't use MyFirstValue
MySecondValue MyEnum = "MySecondValue"
)

// +kubebuilder:validation:Minimum=1
type PositiveInt int

Expand Down
19 changes: 19 additions & 0 deletions test/expected.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -243,11 +243,30 @@ UniqueItems: true +

| *`certificateRef`* __link:https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.SecretObjectReference[$$SecretObjectReference$$]__ | CertificateRef is a reference to a secret containing a certificate + | |
| *`str`* __xref:{anchor_prefix}-github-com-elastic-crd-ref-docs-api-common-commonstring[$$CommonString$$]__ | | |
| *`enum`* __xref:{anchor_prefix}-github-com-elastic-crd-ref-docs-api-v1-myenum[$$MyEnum$$]__ | Enumeration is an example of an aliased enumeration type + | | Enum: [MyFirstValue MySecondValue] +

|===




[id="{anchor_prefix}-github-com-elastic-crd-ref-docs-api-v1-myenum"]
==== MyEnum

_Underlying type:_ _string_



.Validation:
- Enum: [MyFirstValue MySecondValue]

.Appears In:
****
- xref:{anchor_prefix}-github-com-elastic-crd-ref-docs-api-v1-guestbookspec[$$GuestbookSpec$$]
****



[id="{anchor_prefix}-github-com-elastic-crd-ref-docs-api-v1-positiveint"]
==== PositiveInt

Expand Down
18 changes: 18 additions & 0 deletions test/expected.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,10 +180,28 @@ _Appears in:_
| `headers` _[GuestbookHeader](#guestbookheader) array_ | Headers contains a list of header items to include in the page | | MaxItems: 10 <br />UniqueItems: true <br /> |
| `certificateRef` _[SecretObjectReference](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.SecretObjectReference)_ | CertificateRef is a reference to a secret containing a certificate | | |
| `str` _[CommonString](#commonstring)_ | | | |
| `enum` _[MyEnum](#myenum)_ | Enumeration is an example of an aliased enumeration type | | Enum: [MyFirstValue MySecondValue] <br /> |




#### MyEnum

_Underlying type:_ _string_



_Validation:_
- Enum: [MyFirstValue MySecondValue]

_Appears in:_
- [GuestbookSpec](#guestbookspec)

| Field | Description |
| `MyFirstValue` | MyFirstValue is an interesting value to use<br /> |
| `MySecondValue` | MySecondValue is what you use when you can't use MyFirstValue<br /> |


#### PositiveInt

_Underlying type:_ _integer_
Expand Down
39 changes: 18 additions & 21 deletions test/hide.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,8 @@ Package v1 contains API Schema definitions for the webapp v1 API group
| --- | --- | --- | --- |
| `apiVersion` _string_ | `webapp.test.k8s.elastic.co/v1` | | |
| `kind` _string_ | `Embedded` | | |

| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | |

| `a` _string_ | | | |

| `x` _string_ | | | |


Expand All @@ -73,7 +70,6 @@ _Appears in:_

| Field | Description | Default | Validation |
| --- | --- | --- | --- |

| `x` _string_ | | | |


Expand All @@ -91,7 +87,6 @@ _Appears in:_

| Field | Description | Default | Validation |
| --- | --- | --- | --- |

| `x` _string_ | | | |


Expand All @@ -110,9 +105,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.25/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | |

| `spec` _[GuestbookSpec](#guestbookspec)_ | | \{ page:1 \} | |


Expand All @@ -129,13 +122,9 @@ _Appears in:_

| Field | Description | Default | Validation |
| --- | --- | --- | --- |

| `name` _string_ | Name of the guest (pipe \| should be escaped) | | MaxLength: 80 <br />Pattern: `0*[a-z0-9]*[a-z]*[0-9]` <br /> |

| `time` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#time-v1-meta)_ | Time of entry | | |

| `comment` _string_ | Comment by guest. This can be a multi-line comment.<br />Like this one.<br />Now let's test a list:<br />* a<br />* b<br /><br />Another isolated comment.<br /><br />Looks good? | | Pattern: `0*[a-z0-9]*[a-z]*[0-9]*` <br /> |

| `rating` _[Rating](#rating)_ | Rating provided by the guest | | Maximum: 5 <br />Minimum: 1 <br /> |


Expand Down Expand Up @@ -166,9 +155,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.25/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | |

| `items` _[Guestbook](#guestbook) array_ | | | |


Expand All @@ -185,21 +172,33 @@ _Appears in:_

| Field | Description | Default | Validation |
| --- | --- | --- | --- |

| `page` _[PositiveInt](#positiveint)_ | Page indicates the page number | 1 | Minimum: 1 <br /> |

| `entries` _[GuestbookEntry](#guestbookentry) array_ | Entries contain guest book entries for the page | | |

| `selector` _[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#labelselector-v1-meta)_ | Selector selects something | | |

| `headers` _[GuestbookHeader](#guestbookheader) array_ | Headers contains a list of header items to include in the page | | MaxItems: 10 <br />UniqueItems: true <br /> |

| `certificateRef` _[SecretObjectReference](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.SecretObjectReference)_ | CertificateRef is a reference to a secret containing a certificate | | |

| `str` _[CommonString](#commonstring)_ | | | |
| `enum` _[MyEnum](#myenum)_ | Enumeration is an example of an aliased enumeration type | | Enum: [MyFirstValue MySecondValue] <br /> |




#### MyEnum

_Underlying type:_ _string_



_Validation:_
- Enum: [MyFirstValue MySecondValue]

_Appears in:_
- [GuestbookSpec](#guestbookspec)

| Field | Description |
| `MyFirstValue` | MyFirstValue is an interesting value to use<br /> |
| `MySecondValue` | MySecondValue is what you use when you can't use MyFirstValue<br /> |


#### PositiveInt

Expand Down Expand Up @@ -246,9 +245,7 @@ Underlying tests that Underlying1's underlying type is Underlying2 instead of st
| --- | --- | --- | --- |
| `apiVersion` _string_ | `webapp.test.k8s.elastic.co/v1` | | |
| `kind` _string_ | `Underlying` | | |

| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | |

| `a` _[Underlying1](#underlying1)_ | | b | MaxLength: 10 <br /> |


Expand Down
8 changes: 7 additions & 1 deletion test/templates/markdown/type.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,18 @@ _Appears in:_

{{ range $type.Members -}}
{{ with .Markers.hidefromdoc -}}
{{ else }}
{{ else -}}
| `{{ .Name }}` _{{ markdownRenderType .Type }}_ | {{ template "type_members" . }} | {{ markdownRenderDefault .Default }} | {{ range .Validation -}} {{ . }} <br />{{ end }} |
{{ end -}}
{{ end -}}

{{ end -}}

{{ if $type.EnumValues -}}
| Field | Description |
{{ range $type.EnumValues -}}
| `{{ .Name }}` | {{ markdownRenderFieldDoc .Doc }} |
{{ end -}}
{{ end -}}
{{- end -}}
{{- end -}}
7 changes: 7 additions & 0 deletions types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ type Type struct {
ValueType *Type `json:"valueType"` // for maps
Fields Fields `json:"fields"` // for structs
References []*Type `json:"-"` // other types that refer to this type
EnumValues []EnumValue `json:"enumValues"` // for enum values of aliased string types
}

func (t *Type) IsBasic() bool {
Expand Down Expand Up @@ -369,3 +370,9 @@ func (gvd GroupVersionDetails) SortedKinds() []string {

return kindsList
}

// EnumValue describes a constant value for enumerations
type EnumValue struct {
Name string
Doc string
}

0 comments on commit ea9fcaa

Please sign in to comment.