Skip to content

Commit 5b635a6

Browse files
mvdancueckoo
authored andcommitted
internal/encoding/gotypes: implement optional=nillable
This requires recording "facts" about how we generated each Go type, such as whether it came from a `@go(type=)` override or whether the resulting Go type is nillable or not. This now works for inline types, as well as references to generated local types. References to generated types from imported packages is left as a TODO for a follow-up, given the size of this patch. Document the feature as well, and clean up the help text a bit. For example, it didn't use the same "experimental" prefix we already used in other commands like `cue mod mirror`. Fixes #3760. Signed-off-by: Daniel Martí <[email protected]> Change-Id: Ib2dd8ace6eb75462ca9247ca06613ab3ba5bcafd Dispatch-Trailer: {"type":"trybot","CL":1214380,"patchset":1,"ref":"refs/changes/80/1214380/1","targetBranch":"master"}
1 parent 27aac53 commit 5b635a6

File tree

3 files changed

+150
-74
lines changed

3 files changed

+150
-74
lines changed

cmd/cue/cmd/exp.go

+22-17
Original file line numberDiff line numberDiff line change
@@ -40,52 +40,57 @@ as the objective is to gain experience and then move the feature elsewhere.
4040
return cmd
4141
}
4242

43-
// TODO(mvdan): document the "optional" attribute option when finished.
44-
4543
func newExpGenGoTypesCmd(c *Command) *cobra.Command {
4644
cmd := &cobra.Command{
4745
Use: "gengotypes",
4846
Short: "generate Go types from CUE definitions",
49-
Long: `
50-
gengotypes generates Go type definitions from exported CUE definitions.
47+
Long: `WARNING: THIS COMMAND IS EXPERIMENTAL.
5148
52-
*This command is experimental and may be changed at any time - see "cue help exp"*
49+
gengotypes generates Go type definitions from exported CUE definitions.
5350
5451
The generated Go types are guaranteed to accept any value accepted by the CUE definitions,
5552
but may be more general. For example, "string | int" will translate into the Go
56-
type "any" because the Go type system is not able to express
57-
disjunctions.
53+
type "any" because the Go type system is not able to express disjunctions.
5854
5955
To ensure that the resulting Go code works, any imported CUE packages or
6056
referenced CUE definitions are transitively generated as well.
61-
The generated code is placed in cue_types*_gen.go files in the directory of
62-
each CUE package.
57+
Generated code is placed in cue_types*_gen.go files in each CUE package directory.
6358
6459
Generated Go type and field names may differ from the original CUE names by default.
6560
For instance, an exported definition "#foo" becomes "Foo",
66-
given that Go uses capitalization to export names in a package,
67-
and a nested definition like "#foo.#bar" becomes "Foo_Bar",
68-
given that Go does not allow declaring nested types.
61+
and a nested definition like "#foo.#bar" becomes "Foo_Bar".
6962
70-
@go attributes can be used to override which name or type to be generated, for example:
63+
@go attributes can be used to override which name to be generated:
7164
7265
package foo
7366
@go(betterpkgname)
7467
7568
#Bar: {
7669
@go(BetterBarTypeName)
7770
renamed: int @go(BetterFieldName)
78-
79-
retypedLocal: [...string] @go(,type=[]LocalType)
80-
retypedImport: [...string] @go(,type=[]"foo.com/bar".ImportedType)
8171
}
8272
83-
The attribute "@go(-)" can be used to ignore a definition or field, for example:
73+
The attribute "@go(-)" can be used to ignore a definition or field:
8474
8575
#ignoredDefinition: {
8676
@go(-)
8777
}
8878
ignoredField: int @go(-)
79+
80+
"type=" overrides an entire value to generate as a given Go type expression:
81+
82+
retypedLocal: [string]: int @go(,type=map[LocalType]int)
83+
retypedImport: [...string] @go(,type=[]"foo.com/bar".ImportedType)
84+
85+
"optional=" controls how CUE optional fields are generated as Go fields.
86+
The default is "zero", representing a missing field as the zero value.
87+
"nillable" ensures the generated Go type can represent missing fields as nil.
88+
89+
optionalDefault?: int // generates as "int64"
90+
optionalNillable?: int @go(,optional=nillable) // generates as "*int64"
91+
nested: {
92+
@go(,optional=nillable) // set for all fields under this struct
93+
}
8994
`[1:],
9095
// TODO: write a long help text once the feature set is reasonably stable.
9196
RunE: mkRunE(c, runExpGenGoTypes),

cmd/cue/cmd/testdata/script/exp_gengotypes.txtar

+12-12
Original file line numberDiff line numberDiff line change
@@ -287,14 +287,14 @@ func main() {
287287
zeroRoot.Fields.OptionalList = make([]int64, 0)
288288
zeroRoot.Fields.OptionalMap = make(map[string]int64, 0)
289289

290-
zeroRoot.Fields.OptionalTopAttrNillable = (*any)(nil)
291-
zeroRoot.Fields.OptionalNullAttrNillable = (**struct{})(nil)
290+
zeroRoot.Fields.OptionalTopAttrNillable = (any)(nil)
291+
zeroRoot.Fields.OptionalNullAttrNillable = (*struct{})(nil)
292292
zeroRoot.Fields.OptionalBasicAttrNillable = (*int64)(nil)
293-
zeroRoot.Fields.OptionalListAttrNillable = (*[]int64)(nil)
293+
zeroRoot.Fields.OptionalListAttrNillable = ([]int64)(nil)
294294

295-
zeroRoot.Fields.OptionalInlineMapAttrNillable = (*map[string]int64)(nil)
296-
zeroRoot.Fields.OptionalNamedMapAttrNillable = (*root.NamedMap)(nil)
297-
zeroRoot.Fields.OptionalInlineNestedAttrNillable = (*struct{F *[]string `json:"f,omitempty"`})(nil)
295+
zeroRoot.Fields.OptionalInlineMapAttrNillable = (map[string]int64)(nil)
296+
zeroRoot.Fields.OptionalNamedMapAttrNillable = (root.NamedMap)(nil)
297+
zeroRoot.Fields.OptionalInlineNestedAttrNillable = (*struct{F []string `json:"f,omitempty"`})(nil)
298298
zeroRoot.Fields.OptionalNamedNestedAttrNillable = (*root.NamedNested)(nil)
299299

300300
zeroRoot.Fields.OptionalStruct = root.EmptyStruct{}
@@ -720,20 +720,20 @@ type Root struct {
720720

721721
OptionalMap map[string]int64 `json:"optionalMap,omitempty"`
722722

723-
OptionalTopAttrNillable *any/* CUE top */ `json:"optionalTopAttrNillable,omitempty"`
723+
OptionalTopAttrNillable any/* CUE top */ `json:"optionalTopAttrNillable,omitempty"`
724724

725-
OptionalNullAttrNillable **struct{}/* CUE null */ `json:"optionalNullAttrNillable,omitempty"`
725+
OptionalNullAttrNillable *struct{}/* CUE null */ `json:"optionalNullAttrNillable,omitempty"`
726726

727727
OptionalBasicAttrNillable *int64 `json:"optionalBasicAttrNillable,omitempty"`
728728

729-
OptionalListAttrNillable *[]int64 `json:"optionalListAttrNillable,omitempty"`
729+
OptionalListAttrNillable []int64 `json:"optionalListAttrNillable,omitempty"`
730730

731-
OptionalInlineMapAttrNillable *map[string]int64 `json:"optionalInlineMapAttrNillable,omitempty"`
731+
OptionalInlineMapAttrNillable map[string]int64 `json:"optionalInlineMapAttrNillable,omitempty"`
732732

733-
OptionalNamedMapAttrNillable *NamedMap `json:"optionalNamedMapAttrNillable,omitempty"`
733+
OptionalNamedMapAttrNillable NamedMap `json:"optionalNamedMapAttrNillable,omitempty"`
734734

735735
OptionalInlineNestedAttrNillable *struct {
736-
F *[]string `json:"f,omitempty"`
736+
F []string `json:"f,omitempty"`
737737
} `json:"optionalInlineNestedAttrNillable,omitempty"`
738738

739739
OptionalNamedNestedAttrNillable *NamedNested `json:"optionalNamedNestedAttrNillable,omitempty"`

0 commit comments

Comments
 (0)